aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/arm/Samsung-S3C24XX/CPUfreq.txt75
-rw-r--r--Documentation/filesystems/gfs2-uevents.txt100
-rw-r--r--Documentation/filesystems/seq_file.txt2
-rw-r--r--Documentation/flexible-arrays.txt99
-rw-r--r--Documentation/input/sentelic.txt475
-rw-r--r--Documentation/ioctl/ioctl-number.txt2
-rw-r--r--Documentation/kernel-parameters.txt39
-rw-r--r--Documentation/kvm/api.txt759
-rw-r--r--Documentation/vm/slub.txt10
-rw-r--r--MAINTAINERS45
-rw-r--r--arch/arm/Kconfig264
-rw-r--r--arch/arm/Kconfig.debug1
-rw-r--r--arch/arm/Makefile17
-rw-r--r--arch/arm/boot/Makefile9
-rw-r--r--arch/arm/boot/compressed/head.S184
-rw-r--r--arch/arm/common/vic.c95
-rw-r--r--arch/arm/configs/bcmring_defconfig725
-rw-r--r--arch/arm/configs/cpu9260_defconfig1338
-rw-r--r--arch/arm/configs/cpu9g20_defconfig1328
-rw-r--r--arch/arm/configs/cpuat91_defconfig1316
-rw-r--r--arch/arm/configs/jornada720_defconfig329
-rw-r--r--arch/arm/configs/nhk8815_defconfig1316
-rw-r--r--arch/arm/configs/s5pc100_defconfig892
-rw-r--r--arch/arm/include/asm/assembler.h133
-rw-r--r--arch/arm/include/asm/elf.h3
-rw-r--r--arch/arm/include/asm/ftrace.h35
-rw-r--r--arch/arm/include/asm/futex.h1
-rw-r--r--arch/arm/include/asm/mach/mmc.h2
-rw-r--r--arch/arm/include/asm/memory.h23
-rw-r--r--arch/arm/include/asm/mmu_context.h2
-rw-r--r--arch/arm/include/asm/page-nommu.h3
-rw-r--r--arch/arm/include/asm/page.h4
-rw-r--r--arch/arm/include/asm/pgalloc.h16
-rw-r--r--arch/arm/include/asm/pgtable.h37
-rw-r--r--arch/arm/include/asm/ptrace.h8
-rw-r--r--arch/arm/include/asm/thread_info.h2
-rw-r--r--arch/arm/include/asm/uaccess.h7
-rw-r--r--arch/arm/include/asm/unified.h126
-rw-r--r--arch/arm/include/asm/unistd.h7
-rw-r--r--arch/arm/kernel/Makefile4
-rw-r--r--arch/arm/kernel/armksyms.c1
-rw-r--r--arch/arm/kernel/calls.S10
-rw-r--r--arch/arm/kernel/crunch.c13
-rw-r--r--arch/arm/kernel/entry-armv.S179
-rw-r--r--arch/arm/kernel/entry-common.S57
-rw-r--r--arch/arm/kernel/entry-header.S92
-rw-r--r--arch/arm/kernel/head-common.S15
-rw-r--r--arch/arm/kernel/head-nommu.S16
-rw-r--r--arch/arm/kernel/head.S28
-rw-r--r--arch/arm/kernel/irq.c2
-rw-r--r--arch/arm/kernel/module.c53
-rw-r--r--arch/arm/kernel/process.c2
-rw-r--r--arch/arm/kernel/ptrace.c8
-rw-r--r--arch/arm/kernel/return_address.c71
-rw-r--r--arch/arm/kernel/setup.c28
-rw-r--r--arch/arm/kernel/signal.c86
-rw-r--r--arch/arm/kernel/stacktrace.c4
-rw-r--r--arch/arm/kernel/unwind.c4
-rw-r--r--arch/arm/lib/ashldi3.S4
-rw-r--r--arch/arm/lib/ashrdi3.S4
-rw-r--r--arch/arm/lib/backtrace.S8
-rw-r--r--arch/arm/lib/bitops.h2
-rw-r--r--arch/arm/lib/clear_user.S15
-rw-r--r--arch/arm/lib/copy_from_user.S19
-rw-r--r--arch/arm/lib/copy_template.S24
-rw-r--r--arch/arm/lib/copy_to_user.S19
-rw-r--r--arch/arm/lib/csumpartialcopyuser.S48
-rw-r--r--arch/arm/lib/div64.S4
-rw-r--r--arch/arm/lib/findbit.S34
-rw-r--r--arch/arm/lib/getuser.S5
-rw-r--r--arch/arm/lib/io-writesw-armv4.S5
-rw-r--r--arch/arm/lib/lshrdi3.S4
-rw-r--r--arch/arm/lib/memcpy.S7
-rw-r--r--arch/arm/lib/memmove.S28
-rw-r--r--arch/arm/lib/putuser.S15
-rw-r--r--arch/arm/lib/sha1.S2
-rw-r--r--arch/arm/lib/strncpy_from_user.S2
-rw-r--r--arch/arm/lib/strnlen_user.S2
-rw-r--r--arch/arm/mach-at91/Kconfig70
-rw-r--r--arch/arm/mach-at91/Makefile9
-rw-r--r--arch/arm/mach-at91/Makefile.boot4
-rw-r--r--arch/arm/mach-at91/at91sam9260_devices.c116
-rw-r--r--arch/arm/mach-at91/at91sam9261.c22
-rw-r--r--arch/arm/mach-at91/at91sam9263_devices.c14
-rw-r--r--arch/arm/mach-at91/at91sam9g45.c360
-rw-r--r--arch/arm/mach-at91/at91sam9g45_devices.c1230
-rw-r--r--arch/arm/mach-at91/board-afeb-9260v1.c12
-rw-r--r--arch/arm/mach-at91/board-cpu9krea.c385
-rw-r--r--arch/arm/mach-at91/board-cpuat91.c185
-rw-r--r--arch/arm/mach-at91/board-sam9261ek.c14
-rw-r--r--arch/arm/mach-at91/board-sam9263ek.c4
-rw-r--r--arch/arm/mach-at91/board-sam9g20ek.c1
-rw-r--r--arch/arm/mach-at91/board-sam9m10g45ek.c389
-rw-r--r--arch/arm/mach-at91/board-sam9rlek.c79
-rw-r--r--arch/arm/mach-at91/clock.c66
-rw-r--r--arch/arm/mach-at91/generic.h2
-rw-r--r--arch/arm/mach-at91/gpio.c15
-rw-r--r--arch/arm/mach-at91/include/mach/at91sam9261.h3
-rw-r--r--arch/arm/mach-at91/include/mach/at91sam9g45.h155
-rw-r--r--arch/arm/mach-at91/include/mach/at91sam9g45_matrix.h153
-rw-r--r--arch/arm/mach-at91/include/mach/board.h14
-rw-r--r--arch/arm/mach-at91/include/mach/cpu.h23
-rw-r--r--arch/arm/mach-at91/include/mach/hardware.h4
-rw-r--r--arch/arm/mach-at91/include/mach/timex.h10
-rw-r--r--arch/arm/mach-at91/pm.c3
-rw-r--r--arch/arm/mach-bcmring/Kconfig21
-rw-r--r--arch/arm/mach-bcmring/Makefile8
-rw-r--r--arch/arm/mach-bcmring/Makefile.boot6
-rw-r--r--arch/arm/mach-bcmring/arch.c157
-rw-r--r--arch/arm/mach-bcmring/clock.c224
-rw-r--r--arch/arm/mach-bcmring/clock.h33
-rw-r--r--arch/arm/mach-bcmring/core.c367
-rw-r--r--arch/arm/mach-bcmring/core.h30
-rw-r--r--arch/arm/mach-bcmring/csp/Makefile3
-rw-r--r--arch/arm/mach-bcmring/csp/chipc/Makefile1
-rw-r--r--arch/arm/mach-bcmring/csp/chipc/chipcHw.c776
-rw-r--r--arch/arm/mach-bcmring/csp/chipc/chipcHw_init.c293
-rw-r--r--arch/arm/mach-bcmring/csp/chipc/chipcHw_reset.c124
-rw-r--r--arch/arm/mach-bcmring/csp/chipc/chipcHw_str.c64
-rw-r--r--arch/arm/mach-bcmring/csp/dmac/Makefile1
-rw-r--r--arch/arm/mach-bcmring/csp/dmac/dmacHw.c917
-rw-r--r--arch/arm/mach-bcmring/csp/dmac/dmacHw_extra.c1017
-rw-r--r--arch/arm/mach-bcmring/csp/tmr/Makefile1
-rw-r--r--arch/arm/mach-bcmring/csp/tmr/tmrHw.c576
-rw-r--r--arch/arm/mach-bcmring/dma.c2321
-rw-r--r--arch/arm/mach-bcmring/dma_device.c593
-rw-r--r--arch/arm/mach-bcmring/include/cfg_global.h13
-rw-r--r--arch/arm/mach-bcmring/include/cfg_global_defines.h40
-rw-r--r--arch/arm/mach-bcmring/include/csp/cache.h35
-rw-r--r--arch/arm/mach-bcmring/include/csp/delay.h36
-rw-r--r--arch/arm/mach-bcmring/include/csp/dmacHw.h596
-rw-r--r--arch/arm/mach-bcmring/include/csp/errno.h32
-rw-r--r--arch/arm/mach-bcmring/include/csp/intcHw.h40
-rw-r--r--arch/arm/mach-bcmring/include/csp/module.h32
-rw-r--r--arch/arm/mach-bcmring/include/csp/reg.h114
-rw-r--r--arch/arm/mach-bcmring/include/csp/secHw.h65
-rw-r--r--arch/arm/mach-bcmring/include/csp/stdint.h30
-rw-r--r--arch/arm/mach-bcmring/include/csp/string.h34
-rw-r--r--arch/arm/mach-bcmring/include/csp/tmrHw.h263
-rw-r--r--arch/arm/mach-bcmring/include/mach/clkdev.h7
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/cap.h63
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/cap_inline.h409
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/chipcHw_def.h1123
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/chipcHw_inline.h1673
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/chipcHw_reg.h530
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/ddrcReg.h872
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/dmacHw_priv.h145
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/dmacHw_reg.h406
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/hw_cfg.h73
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/intcHw_reg.h246
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/mm_addr.h101
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/mm_io.h147
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/secHw_def.h100
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/secHw_inline.h79
-rw-r--r--arch/arm/mach-bcmring/include/mach/csp/tmrHw_reg.h82
-rw-r--r--arch/arm/mach-bcmring/include/mach/dma.h826
-rw-r--r--arch/arm/mach-bcmring/include/mach/entry-macro.S86
-rw-r--r--arch/arm/mach-bcmring/include/mach/hardware.h60
-rw-r--r--arch/arm/mach-bcmring/include/mach/io.h56
-rw-r--r--arch/arm/mach-bcmring/include/mach/irqs.h132
-rw-r--r--arch/arm/mach-bcmring/include/mach/memory.h33
-rw-r--r--arch/arm/mach-bcmring/include/mach/memory_settings.h67
-rw-r--r--arch/arm/mach-bcmring/include/mach/system.h54
-rw-r--r--arch/arm/mach-bcmring/include/mach/timer.h77
-rw-r--r--arch/arm/mach-bcmring/include/mach/timex.h25
-rw-r--r--arch/arm/mach-bcmring/include/mach/uncompress.h43
-rw-r--r--arch/arm/mach-bcmring/include/mach/vmalloc.h25
-rw-r--r--arch/arm/mach-bcmring/irq.c127
-rw-r--r--arch/arm/mach-bcmring/mm.c56
-rw-r--r--arch/arm/mach-bcmring/timer.c62
-rw-r--r--arch/arm/mach-ep93xx/adssphere.c11
-rw-r--r--arch/arm/mach-ep93xx/clock.c131
-rw-r--r--arch/arm/mach-ep93xx/core.c202
-rw-r--r--arch/arm/mach-ep93xx/edb93xx.c10
-rw-r--r--arch/arm/mach-ep93xx/gesbc9312.c11
-rw-r--r--arch/arm/mach-ep93xx/gpio.c65
-rw-r--r--arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h103
-rw-r--r--arch/arm/mach-ep93xx/include/mach/hardware.h17
-rw-r--r--arch/arm/mach-ep93xx/include/mach/io.h17
-rw-r--r--arch/arm/mach-ep93xx/include/mach/platform.h20
-rw-r--r--arch/arm/mach-ep93xx/include/mach/system.h12
-rw-r--r--arch/arm/mach-ep93xx/include/mach/ts72xx.h1
-rw-r--r--arch/arm/mach-ep93xx/micro9.c11
-rw-r--r--arch/arm/mach-ep93xx/ts72xx.c15
-rw-r--r--arch/arm/mach-integrator/include/mach/hardware.h4
-rw-r--r--arch/arm/mach-integrator/integrator_cp.c18
-rw-r--r--arch/arm/mach-kirkwood/Kconfig6
-rw-r--r--arch/arm/mach-kirkwood/Makefile1
-rw-r--r--arch/arm/mach-kirkwood/common.c5
-rw-r--r--arch/arm/mach-kirkwood/include/mach/kirkwood.h1
-rw-r--r--arch/arm/mach-kirkwood/openrd_base-setup.c84
-rw-r--r--arch/arm/mach-mx1/clock.c86
-rw-r--r--arch/arm/mach-mx1/devices.c87
-rw-r--r--arch/arm/mach-mx1/generic.c7
-rw-r--r--arch/arm/mach-mx1/mx1ads.c10
-rw-r--r--arch/arm/mach-mx1/scb9328.c16
-rw-r--r--arch/arm/mach-mx2/Kconfig35
-rw-r--r--arch/arm/mach-mx2/Makefile3
-rw-r--r--arch/arm/mach-mx2/clock_imx21.c2
-rw-r--r--arch/arm/mach-mx2/clock_imx27.c11
-rw-r--r--arch/arm/mach-mx2/devices.c332
-rw-r--r--arch/arm/mach-mx2/devices.h9
-rw-r--r--arch/arm/mach-mx2/eukrea_cpuimx27.c234
-rw-r--r--arch/arm/mach-mx2/eukrea_mbimx27-baseboard.c249
-rw-r--r--arch/arm/mach-mx2/generic.c12
-rw-r--r--arch/arm/mach-mx2/mx21ads.c44
-rw-r--r--arch/arm/mach-mx2/mx27ads.c38
-rw-r--r--arch/arm/mach-mx2/mx27lite.c2
-rw-r--r--arch/arm/mach-mx2/mx27pdk.c2
-rw-r--r--arch/arm/mach-mx2/pca100.c244
-rw-r--r--arch/arm/mach-mx2/pcm038.c17
-rw-r--r--arch/arm/mach-mx2/pcm970-baseboard.c112
-rw-r--r--arch/arm/mach-mx25/Kconfig9
-rw-r--r--arch/arm/mach-mx25/Makefile3
-rw-r--r--arch/arm/mach-mx25/Makefile.boot3
-rw-r--r--arch/arm/mach-mx25/clock.c219
-rw-r--r--arch/arm/mach-mx25/devices.c402
-rw-r--r--arch/arm/mach-mx25/devices.h19
-rw-r--r--arch/arm/mach-mx25/mm.c76
-rw-r--r--arch/arm/mach-mx25/mx25pdk.c58
-rw-r--r--arch/arm/mach-mx3/armadillo5x0.c2
-rw-r--r--arch/arm/mach-mx3/clock-imx35.c22
-rw-r--r--arch/arm/mach-mx3/clock.c22
-rw-r--r--arch/arm/mach-mx3/devices.c163
-rw-r--r--arch/arm/mach-mx3/devices.h6
-rw-r--r--arch/arm/mach-mx3/mm.c14
-rw-r--r--arch/arm/mach-mx3/mx31ads.c2
-rw-r--r--arch/arm/mach-mx3/mx31lilly.c2
-rw-r--r--arch/arm/mach-mx3/mx31lite.c7
-rw-r--r--arch/arm/mach-mx3/mx31moboard-devboard.c43
-rw-r--r--arch/arm/mach-mx3/mx31moboard-marxbot.c55
-rw-r--r--arch/arm/mach-mx3/mx31moboard.c134
-rw-r--r--arch/arm/mach-mx3/mx31pdk.c2
-rw-r--r--arch/arm/mach-mx3/mx35pdk.c2
-rw-r--r--arch/arm/mach-mx3/pcm037.c42
-rw-r--r--arch/arm/mach-mx3/pcm043.c8
-rw-r--r--arch/arm/mach-mx3/qong.c7
-rw-r--r--arch/arm/mach-mxc91231/Kconfig11
-rw-r--r--arch/arm/mach-mxc91231/Makefile2
-rw-r--r--arch/arm/mach-mxc91231/Makefile.boot3
-rw-r--r--arch/arm/mach-mxc91231/clock.c642
-rw-r--r--arch/arm/mach-mxc91231/crm_regs.h399
-rw-r--r--arch/arm/mach-mxc91231/devices.c251
-rw-r--r--arch/arm/mach-mxc91231/devices.h13
-rw-r--r--arch/arm/mach-mxc91231/iomux.c177
-rw-r--r--arch/arm/mach-mxc91231/magx-zn5.c63
-rw-r--r--arch/arm/mach-mxc91231/mm.c94
-rw-r--r--arch/arm/mach-mxc91231/system.c51
-rw-r--r--arch/arm/mach-netx/include/mach/entry-macro.S4
-rw-r--r--arch/arm/mach-nomadik/Kconfig21
-rw-r--r--arch/arm/mach-nomadik/Makefile19
-rw-r--r--arch/arm/mach-nomadik/Makefile.boot4
-rw-r--r--arch/arm/mach-nomadik/board-nhk8815.c111
-rw-r--r--arch/arm/mach-nomadik/clock.c45
-rw-r--r--arch/arm/mach-nomadik/clock.h14
-rw-r--r--arch/arm/mach-nomadik/cpu-8815.c139
-rw-r--r--arch/arm/mach-nomadik/gpio.c396
-rw-r--r--arch/arm/mach-nomadik/i2c-8815nhk.c65
-rw-r--r--arch/arm/mach-nomadik/include/mach/clkdev.h7
-rw-r--r--arch/arm/mach-nomadik/include/mach/debug-macro.S22
-rw-r--r--arch/arm/mach-nomadik/include/mach/entry-macro.S43
-rw-r--r--arch/arm/mach-nomadik/include/mach/gpio.h71
-rw-r--r--arch/arm/mach-nomadik/include/mach/hardware.h90
-rw-r--r--arch/arm/mach-nomadik/include/mach/io.h22
-rw-r--r--arch/arm/mach-nomadik/include/mach/irqs.h82
-rw-r--r--arch/arm/mach-nomadik/include/mach/memory.h28
-rw-r--r--arch/arm/mach-nomadik/include/mach/mtu.h45
-rw-r--r--arch/arm/mach-nomadik/include/mach/setup.h22
-rw-r--r--arch/arm/mach-nomadik/include/mach/system.h45
-rw-r--r--arch/arm/mach-nomadik/include/mach/timex.h6
-rw-r--r--arch/arm/mach-nomadik/include/mach/uncompress.h63
-rw-r--r--arch/arm/mach-nomadik/include/mach/vmalloc.h2
-rw-r--r--arch/arm/mach-nomadik/timer.c164
-rw-r--r--arch/arm/mach-omap2/board-4430sdp.c2
-rw-r--r--arch/arm/mach-omap2/mcbsp.c41
-rw-r--r--arch/arm/mach-omap2/serial.c10
-rw-r--r--arch/arm/mach-orion5x/Kconfig21
-rw-r--r--arch/arm/mach-orion5x/Makefile3
-rw-r--r--arch/arm/mach-orion5x/addr-map.c3
-rw-r--r--arch/arm/mach-orion5x/d2net-setup.c365
-rw-r--r--arch/arm/mach-orion5x/net2big-setup.c431
-rw-r--r--arch/arm/mach-pxa/include/mach/pxa27x_keypad.h7
-rw-r--r--arch/arm/mach-pxa/sharpsl_pm.c4
-rw-r--r--arch/arm/mach-realview/Kconfig2
-rw-r--r--arch/arm/mach-realview/core.c7
-rw-r--r--arch/arm/mach-realview/include/mach/gpio.h6
-rw-r--r--arch/arm/mach-realview/include/mach/hardware.h4
-rw-r--r--arch/arm/mach-realview/platsmp.c18
-rw-r--r--arch/arm/mach-realview/realview_eb.c22
-rw-r--r--arch/arm/mach-realview/realview_pb1176.c22
-rw-r--r--arch/arm/mach-realview/realview_pb11mp.c22
-rw-r--r--arch/arm/mach-realview/realview_pba8.c22
-rw-r--r--arch/arm/mach-realview/realview_pbx.c22
-rw-r--r--arch/arm/mach-s3c2410/Kconfig18
-rw-r--r--arch/arm/mach-s3c2410/Makefile2
-rw-r--r--arch/arm/mach-s3c2410/cpu-freq.c159
-rw-r--r--arch/arm/mach-s3c2410/dma.c11
-rw-r--r--arch/arm/mach-s3c2410/include/mach/irqs.h6
-rw-r--r--arch/arm/mach-s3c2410/include/mach/map.h8
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-gpio.h4
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-mem.h10
-rw-r--r--arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h23
-rw-r--r--arch/arm/mach-s3c2410/include/mach/spi.h3
-rw-r--r--arch/arm/mach-s3c2410/irq.c15
-rw-r--r--arch/arm/mach-s3c2410/mach-bast.c41
-rw-r--r--arch/arm/mach-s3c2410/pll.c95
-rw-r--r--arch/arm/mach-s3c2410/pm.c12
-rw-r--r--arch/arm/mach-s3c2410/s3c2410.c29
-rw-r--r--arch/arm/mach-s3c2412/Kconfig9
-rw-r--r--arch/arm/mach-s3c2412/Makefile1
-rw-r--r--arch/arm/mach-s3c2412/cpu-freq.c257
-rw-r--r--arch/arm/mach-s3c2412/s3c2412.c12
-rw-r--r--arch/arm/mach-s3c2440/Kconfig7
-rw-r--r--arch/arm/mach-s3c2440/mach-osiris.c9
-rw-r--r--arch/arm/mach-s3c24a0/include/mach/map.h1
-rw-r--r--arch/arm/mach-s3c6400/include/mach/map.h8
-rw-r--r--arch/arm/mach-s3c6400/s3c6400.c2
-rw-r--r--arch/arm/mach-s3c6410/Kconfig10
-rw-r--r--arch/arm/mach-s3c6410/Makefile3
-rw-r--r--arch/arm/mach-s3c6410/cpu.c2
-rw-r--r--arch/arm/mach-s3c6410/mach-hmt.c276
-rw-r--r--arch/arm/mach-s3c6410/mach-ncp.c2
-rw-r--r--arch/arm/mach-s3c6410/mach-smdk6410.c26
-rw-r--r--arch/arm/mach-s5pc100/Kconfig22
-rw-r--r--arch/arm/mach-s5pc100/Makefile17
-rw-r--r--arch/arm/mach-s5pc100/Makefile.boot2
-rw-r--r--arch/arm/mach-s5pc100/cpu.c97
-rw-r--r--arch/arm/mach-s5pc100/include/mach/debug-macro.S38
-rw-r--r--arch/arm/mach-s5pc100/include/mach/entry-macro.S50
-rw-r--r--arch/arm/mach-s5pc100/include/mach/gpio-core.h21
-rw-r--r--arch/arm/mach-s5pc100/include/mach/gpio.h146
-rw-r--r--arch/arm/mach-s5pc100/include/mach/hardware.h14
-rw-r--r--arch/arm/mach-s5pc100/include/mach/irqs.h14
-rw-r--r--arch/arm/mach-s5pc100/include/mach/map.h75
-rw-r--r--arch/arm/mach-s5pc100/include/mach/memory.h18
-rw-r--r--arch/arm/mach-s5pc100/include/mach/pwm-clock.h56
-rw-r--r--arch/arm/mach-s5pc100/include/mach/regs-irq.h24
-rw-r--r--arch/arm/mach-s5pc100/include/mach/system.h24
-rw-r--r--arch/arm/mach-s5pc100/include/mach/tick.h29
-rw-r--r--arch/arm/mach-s5pc100/include/mach/uncompress.h28
-rw-r--r--arch/arm/mach-s5pc100/mach-smdkc100.c103
-rw-r--r--arch/arm/mach-u300/mmc.c2
-rw-r--r--arch/arm/mach-versatile/core.c17
-rw-r--r--arch/arm/mach-versatile/include/mach/gpio.h6
-rw-r--r--arch/arm/mach-versatile/include/mach/irqs.h11
-rw-r--r--arch/arm/mach-versatile/versatile_pb.c17
-rw-r--r--arch/arm/mach-w90x900/Kconfig30
-rw-r--r--arch/arm/mach-w90x900/Makefile12
-rw-r--r--arch/arm/mach-w90x900/clksel.c91
-rw-r--r--arch/arm/mach-w90x900/clock.c26
-rw-r--r--arch/arm/mach-w90x900/clock.h12
-rw-r--r--arch/arm/mach-w90x900/cpu.c212
-rw-r--r--arch/arm/mach-w90x900/cpu.h49
-rw-r--r--arch/arm/mach-w90x900/dev.c389
-rw-r--r--arch/arm/mach-w90x900/gpio.c78
-rw-r--r--arch/arm/mach-w90x900/include/mach/regs-clock.h22
-rw-r--r--arch/arm/mach-w90x900/include/mach/regs-ebi.h33
-rw-r--r--arch/arm/mach-w90x900/include/mach/w90p910_keypad.h15
-rw-r--r--arch/arm/mach-w90x900/irq.c172
-rw-r--r--arch/arm/mach-w90x900/mach-nuc910evb.c44
-rw-r--r--arch/arm/mach-w90x900/mach-nuc950evb.c44
-rw-r--r--arch/arm/mach-w90x900/mach-nuc960evb.c44
-rw-r--r--arch/arm/mach-w90x900/mach-w90p910evb.c267
-rw-r--r--arch/arm/mach-w90x900/mfp-w90p910.c116
-rw-r--r--arch/arm/mach-w90x900/mfp.c158
-rw-r--r--arch/arm/mach-w90x900/nuc910.c60
-rw-r--r--arch/arm/mach-w90x900/nuc910.h28
-rw-r--r--arch/arm/mach-w90x900/nuc950.c54
-rw-r--r--arch/arm/mach-w90x900/nuc950.h28
-rw-r--r--arch/arm/mach-w90x900/nuc960.c54
-rw-r--r--arch/arm/mach-w90x900/nuc960.h28
-rw-r--r--arch/arm/mach-w90x900/time.c153
-rw-r--r--arch/arm/mach-w90x900/w90p910.c136
-rw-r--r--arch/arm/mm/Kconfig2
-rw-r--r--arch/arm/mm/alignment.c20
-rw-r--r--arch/arm/mm/cache-v7.S16
-rw-r--r--arch/arm/mm/dma-mapping.c94
-rw-r--r--arch/arm/mm/fault.c24
-rw-r--r--arch/arm/mm/flush.c9
-rw-r--r--arch/arm/mm/highmem.c8
-rw-r--r--arch/arm/mm/init.c32
-rw-r--r--arch/arm/mm/nommu.c1
-rw-r--r--arch/arm/mm/proc-macros.S8
-rw-r--r--arch/arm/mm/proc-v7.S7
-rw-r--r--arch/arm/plat-mxc/Kconfig17
-rw-r--r--arch/arm/plat-mxc/clock.c170
-rw-r--r--arch/arm/plat-mxc/gpio.c42
-rw-r--r--arch/arm/plat-mxc/include/mach/board-armadillo5x0.h7
-rw-r--r--arch/arm/plat-mxc/include/mach/board-eukrea_cpuimx27.h40
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx21ads.h6
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx27ads.h6
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx27lite.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx27pdk.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx31ads.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx31lilly.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx31lite.h3
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx31moboard.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx31pdk.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/board-mx35pdk.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/board-pcm037.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/board-pcm038.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/board-pcm043.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/board-qong.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/common.h19
-rw-r--r--arch/arm/plat-mxc/include/mach/debug-macro.S68
-rw-r--r--arch/arm/plat-mxc/include/mach/entry-macro.S3
-rw-r--r--arch/arm/plat-mxc/include/mach/hardware.h8
-rw-r--r--arch/arm/plat-mxc/include/mach/imxfb.h29
-rw-r--r--arch/arm/plat-mxc/include/mach/iomux-mx25.h517
-rw-r--r--arch/arm/plat-mxc/include/mach/iomux-mx3.h25
-rw-r--r--arch/arm/plat-mxc/include/mach/iomux-mxc91231.h287
-rw-r--r--arch/arm/plat-mxc/include/mach/iomux-v3.h35
-rw-r--r--arch/arm/plat-mxc/include/mach/iomux.h6
-rw-r--r--arch/arm/plat-mxc/include/mach/irqs.h4
-rw-r--r--arch/arm/plat-mxc/include/mach/memory.h4
-rw-r--r--arch/arm/plat-mxc/include/mach/mx1.h22
-rw-r--r--arch/arm/plat-mxc/include/mach/mx21.h5
-rw-r--r--arch/arm/plat-mxc/include/mach/mx25.h44
-rw-r--r--arch/arm/plat-mxc/include/mach/mx27.h7
-rw-r--r--arch/arm/plat-mxc/include/mach/mx2x.h18
-rw-r--r--arch/arm/plat-mxc/include/mach/mx31.h2
-rw-r--r--arch/arm/plat-mxc/include/mach/mx35.h1
-rw-r--r--arch/arm/plat-mxc/include/mach/mx3x.h21
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc.h28
-rw-r--r--arch/arm/plat-mxc/include/mach/mxc91231.h315
-rw-r--r--arch/arm/plat-mxc/include/mach/system.h10
-rw-r--r--arch/arm/plat-mxc/include/mach/timex.h4
-rw-r--r--arch/arm/plat-mxc/include/mach/uncompress.h68
-rw-r--r--arch/arm/plat-mxc/iomux-v3.c15
-rw-r--r--arch/arm/plat-mxc/irq.c6
-rw-r--r--arch/arm/plat-mxc/pwm.c19
-rw-r--r--arch/arm/plat-mxc/system.c29
-rw-r--r--arch/arm/plat-mxc/time.c39
-rw-r--r--arch/arm/plat-omap/gpio.c249
-rw-r--r--arch/arm/plat-omap/include/mach/dma.h88
-rw-r--r--arch/arm/plat-omap/include/mach/mcbsp.h8
-rw-r--r--arch/arm/plat-omap/mcbsp.c2
-rw-r--r--arch/arm/plat-s3c/Kconfig5
-rw-r--r--arch/arm/plat-s3c/Makefile6
-rw-r--r--arch/arm/plat-s3c/dev-nand.c30
-rw-r--r--arch/arm/plat-s3c/include/plat/adc.h8
-rw-r--r--arch/arm/plat-s3c/include/plat/cpu-freq.h87
-rw-r--r--arch/arm/plat-s3c/include/plat/cpu.h1
-rw-r--r--arch/arm/plat-s3c/include/plat/devs.h3
-rw-r--r--arch/arm/plat-s3c/include/plat/hwmon.h41
-rw-r--r--arch/arm/plat-s3c/include/plat/map-base.h8
-rw-r--r--arch/arm/plat-s3c/pwm.c (renamed from arch/arm/plat-s3c24xx/pwm.c)5
-rw-r--r--arch/arm/plat-s3c24xx/Kconfig66
-rw-r--r--arch/arm/plat-s3c24xx/Makefile12
-rw-r--r--arch/arm/plat-s3c24xx/adc.c64
-rw-r--r--arch/arm/plat-s3c24xx/cpu-freq-debugfs.c199
-rw-r--r--arch/arm/plat-s3c24xx/cpu-freq.c716
-rw-r--r--arch/arm/plat-s3c24xx/cpu.c2
-rw-r--r--arch/arm/plat-s3c24xx/devs.c71
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h282
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/fiq.h13
-rw-r--r--arch/arm/plat-s3c24xx/include/plat/s3c2410.h1
-rw-r--r--arch/arm/plat-s3c24xx/irq.c36
-rw-r--r--arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c64
-rw-r--r--arch/arm/plat-s3c24xx/s3c2410-iotiming.c477
-rw-r--r--arch/arm/plat-s3c24xx/s3c2412-iotiming.c285
-rw-r--r--arch/arm/plat-s3c24xx/s3c2440-cpufreq.c311
-rw-r--r--arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c97
-rw-r--r--arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c127
-rw-r--r--arch/arm/plat-s3c24xx/spi-bus1-gpd8_9_10.c38
-rw-r--r--arch/arm/plat-s3c64xx/Kconfig1
-rw-r--r--arch/arm/plat-s3c64xx/Makefile3
-rw-r--r--arch/arm/plat-s3c64xx/dev-audio.c (renamed from arch/arm/plat-s3c/dev-audio.c)0
-rw-r--r--arch/arm/plat-s5pc1xx/Kconfig50
-rw-r--r--arch/arm/plat-s5pc1xx/Makefile26
-rw-r--r--arch/arm/plat-s5pc1xx/cpu.c112
-rw-r--r--arch/arm/plat-s5pc1xx/dev-uart.c174
-rw-r--r--arch/arm/plat-s5pc1xx/include/plat/irqs.h182
-rw-r--r--arch/arm/plat-s5pc1xx/include/plat/pll.h38
-rw-r--r--arch/arm/plat-s5pc1xx/include/plat/regs-clock.h421
-rw-r--r--arch/arm/plat-s5pc1xx/include/plat/s5pc100.h65
-rw-r--r--arch/arm/plat-s5pc1xx/irq.c259
-rw-r--r--arch/arm/plat-s5pc1xx/s5pc100-clock.c1139
-rw-r--r--arch/arm/plat-s5pc1xx/s5pc100-init.c27
-rw-r--r--arch/arm/plat-s5pc1xx/setup-i2c0.c25
-rw-r--r--arch/arm/plat-s5pc1xx/setup-i2c1.c25
-rw-r--r--arch/arm/tools/mach-types139
-rw-r--r--arch/arm/vfp/entry.S2
-rw-r--r--arch/arm/vfp/vfphw.S48
-rw-r--r--arch/blackfin/include/asm/bfin_rotary.h39
-rw-r--r--arch/ia64/include/asm/kvm_host.h4
-rw-r--r--arch/ia64/include/asm/kvm_para.h4
-rw-r--r--arch/ia64/kernel/head.S1
-rw-r--r--arch/ia64/kernel/head.h1
-rw-r--r--arch/ia64/kernel/process.c7
-rw-r--r--arch/ia64/kvm/Kconfig11
-rw-r--r--arch/ia64/kvm/kvm-ia64.c85
-rw-r--r--arch/ia64/kvm/vcpu.c4
-rw-r--r--arch/powerpc/include/asm/kvm_host.h4
-rw-r--r--arch/powerpc/kvm/44x.c4
-rw-r--r--arch/powerpc/kvm/44x_tlb.c11
-rw-r--r--arch/powerpc/kvm/Kconfig14
-rw-r--r--arch/powerpc/kvm/Makefile4
-rw-r--r--arch/powerpc/kvm/booke.c2
-rw-r--r--arch/powerpc/kvm/e500.c7
-rw-r--r--arch/powerpc/kvm/e500_emulate.c3
-rw-r--r--arch/powerpc/kvm/e500_tlb.c26
-rw-r--r--arch/powerpc/kvm/e500_tlb.h6
-rw-r--r--arch/powerpc/kvm/emulate.c7
-rw-r--r--arch/powerpc/kvm/powerpc.c32
-rw-r--r--arch/powerpc/kvm/trace.h104
-rw-r--r--arch/s390/include/asm/kvm.h9
-rw-r--r--arch/s390/include/asm/kvm_host.h15
-rw-r--r--arch/s390/include/asm/kvm_para.h4
-rw-r--r--arch/s390/include/asm/timex.h8
-rw-r--r--arch/s390/kernel/debug.c8
-rw-r--r--arch/s390/kernel/time.c1
-rw-r--r--arch/s390/kvm/Kconfig9
-rw-r--r--arch/s390/kvm/gaccess.h23
-rw-r--r--arch/s390/kvm/intercept.c18
-rw-r--r--arch/s390/kvm/interrupt.c8
-rw-r--r--arch/s390/kvm/kvm-s390.c78
-rw-r--r--arch/s390/kvm/kvm-s390.h32
-rw-r--r--arch/s390/kvm/sigp.c60
-rw-r--r--arch/x86/include/asm/apicdef.h2
-rw-r--r--arch/x86/include/asm/kvm.h10
-rw-r--r--arch/x86/include/asm/kvm_emulate.h (renamed from arch/x86/include/asm/kvm_x86_emulate.h)0
-rw-r--r--arch/x86/include/asm/kvm_host.h60
-rw-r--r--arch/x86/include/asm/kvm_para.h2
-rw-r--r--arch/x86/include/asm/msr-index.h1
-rw-r--r--arch/x86/include/asm/vmx.h8
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce.c12
-rw-r--r--arch/x86/kernel/kvm.c7
-rw-r--r--arch/x86/kernel/kvmclock.c4
-rw-r--r--arch/x86/kvm/Kconfig21
-rw-r--r--arch/x86/kvm/Makefile35
-rw-r--r--arch/x86/kvm/emulate.c (renamed from arch/x86/kvm/x86_emulate.c)265
-rw-r--r--arch/x86/kvm/i8254.c160
-rw-r--r--arch/x86/kvm/i8254.h5
-rw-r--r--arch/x86/kvm/i8259.c116
-rw-r--r--arch/x86/kvm/irq.h1
-rw-r--r--arch/x86/kvm/kvm_cache_regs.h9
-rw-r--r--arch/x86/kvm/kvm_svm.h51
-rw-r--r--arch/x86/kvm/kvm_timer.h2
-rw-r--r--arch/x86/kvm/lapic.c334
-rw-r--r--arch/x86/kvm/lapic.h4
-rw-r--r--arch/x86/kvm/mmu.c587
-rw-r--r--arch/x86/kvm/mmu.h4
-rw-r--r--arch/x86/kvm/mmutrace.h220
-rw-r--r--arch/x86/kvm/paging_tmpl.h141
-rw-r--r--arch/x86/kvm/svm.c889
-rw-r--r--arch/x86/kvm/timer.c16
-rw-r--r--arch/x86/kvm/trace.h355
-rw-r--r--arch/x86/kvm/vmx.c497
-rw-r--r--arch/x86/kvm/x86.c815
-rw-r--r--arch/x86/kvm/x86.h4
-rw-r--r--arch/x86/mm/highmem_32.c1
-rw-r--r--block/Makefile2
-rw-r--r--block/blk-barrier.c31
-rw-r--r--block/blk-core.c166
-rw-r--r--block/blk-iopoll.c227
-rw-r--r--block/blk-merge.c51
-rw-r--r--block/blk-settings.c21
-rw-r--r--block/blk-sysfs.c7
-rw-r--r--block/blk.h1
-rw-r--r--block/cfq-iosched.c72
-rw-r--r--block/elevator.c16
-rw-r--r--block/genhd.c22
-rw-r--r--block/ioctl.c49
-rw-r--r--drivers/amba/bus.c26
-rw-r--r--drivers/block/aoe/aoeblk.c3
-rw-r--r--drivers/block/cciss.c4
-rw-r--r--drivers/block/loop.c2
-rw-r--r--drivers/block/paride/pcd.c12
-rw-r--r--drivers/block/sx8.c4
-rw-r--r--drivers/block/viodasd.c12
-rw-r--r--drivers/char/raw.c2
-rw-r--r--drivers/char/serial167.c5
-rw-r--r--drivers/edac/Makefile6
-rw-r--r--drivers/edac/amd64_edac.c328
-rw-r--r--drivers/edac/amd64_edac.h71
-rw-r--r--drivers/edac/amd64_edac_dbg.c2
-rw-r--r--drivers/edac/amd64_edac_err_types.c161
-rw-r--r--drivers/edac/edac_mce_amd.c422
-rw-r--r--drivers/edac/edac_mce_amd.h69
-rw-r--r--drivers/hid/Kconfig29
-rw-r--r--drivers/hid/Makefile6
-rw-r--r--drivers/hid/hid-a4tech.c4
-rw-r--r--drivers/hid/hid-apple.c4
-rw-r--r--drivers/hid/hid-belkin.c4
-rw-r--r--drivers/hid/hid-cherry.c4
-rw-r--r--drivers/hid/hid-chicony.c4
-rw-r--r--drivers/hid/hid-core.c71
-rw-r--r--drivers/hid/hid-cypress.c4
-rw-r--r--drivers/hid/hid-debug.c439
-rw-r--r--drivers/hid/hid-ezkey.c4
-rw-r--r--drivers/hid/hid-gyration.c4
-rw-r--r--drivers/hid/hid-ids.h14
-rw-r--r--drivers/hid/hid-input.c13
-rw-r--r--drivers/hid/hid-kensington.c4
-rw-r--r--drivers/hid/hid-kye.c4
-rw-r--r--drivers/hid/hid-lg.c6
-rw-r--r--drivers/hid/hid-lgff.c6
-rw-r--r--drivers/hid/hid-microsoft.c4
-rw-r--r--drivers/hid/hid-monterey.c4
-rw-r--r--drivers/hid/hid-ntrig.c37
-rw-r--r--drivers/hid/hid-petalynx.c4
-rw-r--r--drivers/hid/hid-pl.c4
-rw-r--r--drivers/hid/hid-samsung.c43
-rw-r--r--drivers/hid/hid-sjoy.c4
-rw-r--r--drivers/hid/hid-sony.c4
-rw-r--r--drivers/hid/hid-sunplus.c4
-rw-r--r--drivers/hid/hid-tmff.c10
-rw-r--r--drivers/hid/hid-topseed.c4
-rw-r--r--drivers/hid/hid-twinhan.c147
-rw-r--r--drivers/hid/hid-wacom.c4
-rw-r--r--drivers/hid/hid-zpff.c4
-rw-r--r--drivers/hid/usbhid/hid-core.c12
-rw-r--r--drivers/hid/usbhid/hid-quirks.c2
-rw-r--r--drivers/hid/usbhid/hiddev.c2
-rw-r--r--drivers/hwmon/Kconfig17
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/s3c-hwmon.c405
-rw-r--r--drivers/input/joydev.c106
-rw-r--r--drivers/input/joystick/xpad.c2
-rw-r--r--drivers/input/keyboard/Kconfig26
-rw-r--r--drivers/input/keyboard/Makefile2
-rw-r--r--drivers/input/keyboard/atkbd.c43
-rw-r--r--drivers/input/keyboard/bf54x-keys.c26
-rw-r--r--drivers/input/keyboard/gpio_keys.c19
-rw-r--r--drivers/input/keyboard/hil_kbd.c607
-rw-r--r--drivers/input/keyboard/lkkbd.c62
-rw-r--r--drivers/input/keyboard/matrix_keypad.c15
-rw-r--r--drivers/input/keyboard/pxa27x_keypad.c217
-rw-r--r--drivers/input/keyboard/sh_keysc.c22
-rw-r--r--drivers/input/keyboard/sunkbd.c142
-rw-r--r--drivers/input/keyboard/tosakbd.c18
-rw-r--r--drivers/input/keyboard/twl4030_keypad.c480
-rw-r--r--drivers/input/keyboard/w90p910_keypad.c281
-rw-r--r--drivers/input/misc/Kconfig10
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/bfin_rotary.c283
-rw-r--r--drivers/input/misc/cobalt_btns.c2
-rw-r--r--drivers/input/misc/dm355evm_keys.c42
-rw-r--r--drivers/input/misc/wistron_btns.c84
-rw-r--r--drivers/input/mouse/Kconfig16
-rw-r--r--drivers/input/mouse/Makefile2
-rw-r--r--drivers/input/mouse/alps.c20
-rw-r--r--drivers/input/mouse/alps.h4
-rw-r--r--drivers/input/mouse/bcm5974.c31
-rw-r--r--drivers/input/mouse/elantech.c2
-rw-r--r--drivers/input/mouse/elantech.h4
-rw-r--r--drivers/input/mouse/hgpk.c61
-rw-r--r--drivers/input/mouse/hgpk.h6
-rw-r--r--drivers/input/mouse/hil_ptr.c447
-rw-r--r--drivers/input/mouse/lifebook.c8
-rw-r--r--drivers/input/mouse/lifebook.h4
-rw-r--r--drivers/input/mouse/logips2pp.c41
-rw-r--r--drivers/input/mouse/logips2pp.h4
-rw-r--r--drivers/input/mouse/psmouse-base.c113
-rw-r--r--drivers/input/mouse/psmouse.h30
-rw-r--r--drivers/input/mouse/sentelic.c867
-rw-r--r--drivers/input/mouse/sentelic.h98
-rw-r--r--drivers/input/mouse/synaptics.c34
-rw-r--r--drivers/input/mouse/synaptics.h2
-rw-r--r--drivers/input/mouse/touchkit_ps2.c4
-rw-r--r--drivers/input/mouse/touchkit_ps2.h4
-rw-r--r--drivers/input/mouse/trackpoint.c2
-rw-r--r--drivers/input/mouse/trackpoint.h4
-rw-r--r--drivers/input/mouse/vsxxxaa.c8
-rw-r--r--drivers/input/serio/at32psif.c2
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h71
-rw-r--r--drivers/input/serio/i8042.c242
-rw-r--r--drivers/input/serio/libps2.c15
-rw-r--r--drivers/input/serio/serio.c23
-rw-r--r--drivers/input/touchscreen/Kconfig16
-rw-r--r--drivers/input/touchscreen/atmel_tsadcc.c8
-rw-r--r--drivers/input/touchscreen/eeti_ts.c22
-rw-r--r--drivers/input/touchscreen/h3600_ts_input.c9
-rw-r--r--drivers/input/touchscreen/mainstone-wm97xx.c53
-rw-r--r--drivers/input/touchscreen/tsc2007.c264
-rw-r--r--drivers/input/touchscreen/ucb1400_ts.c5
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c81
-rw-r--r--drivers/input/touchscreen/w90p910_ts.c38
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c121
-rw-r--r--drivers/input/touchscreen/wm97xx-core.c6
-rw-r--r--drivers/md/dm-mpath.c42
-rw-r--r--drivers/md/dm-raid1.c2
-rw-r--r--drivers/md/dm-stripe.c4
-rw-r--r--drivers/md/dm.c28
-rw-r--r--drivers/md/linear.c2
-rw-r--r--drivers/md/multipath.c4
-rw-r--r--drivers/md/raid0.c2
-rw-r--r--drivers/md/raid1.c14
-rw-r--r--drivers/md/raid10.c6
-rw-r--r--drivers/md/raid5.c2
-rw-r--r--drivers/message/fusion/mptbase.c94
-rw-r--r--drivers/message/fusion/mptbase.h21
-rw-r--r--drivers/message/fusion/mptfc.c19
-rw-r--r--drivers/message/fusion/mptsas.c62
-rw-r--r--drivers/message/fusion/mptscsih.c67
-rw-r--r--drivers/message/fusion/mptscsih.h1
-rw-r--r--drivers/message/fusion/mptspi.c21
-rw-r--r--drivers/misc/Kconfig13
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/enclosure.c69
-rw-r--r--drivers/misc/ep93xx_pwm.c384
-rw-r--r--drivers/mmc/host/mmci.c79
-rw-r--r--drivers/mmc/host/mmci.h2
-rw-r--r--drivers/mtd/nand/ts7250.c5
-rw-r--r--drivers/net/Kconfig2
-rw-r--r--drivers/s390/scsi/zfcp_aux.c288
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c94
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c544
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h175
-rw-r--r--drivers/s390/scsi/zfcp_def.h183
-rw-r--r--drivers/s390/scsi/zfcp_erp.c155
-rw-r--r--drivers/s390/scsi/zfcp_ext.h102
-rw-r--r--drivers/s390/scsi/zfcp_fc.c176
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c635
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h3
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c369
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c73
-rw-r--r--drivers/s390/scsi/zfcp_sysfs.c34
-rw-r--r--drivers/scsi/Kconfig6
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/bnx2i/bnx2i_init.c100
-rw-r--r--drivers/scsi/bnx2i/bnx2i_iscsi.c13
-rw-r--r--drivers/scsi/ch.c6
-rw-r--r--drivers/scsi/constants.c95
-rw-r--r--drivers/scsi/device_handler/scsi_dh.c56
-rw-r--r--drivers/scsi/device_handler/scsi_dh_alua.c2
-rw-r--r--drivers/scsi/device_handler/scsi_dh_emc.c59
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c116
-rw-r--r--drivers/scsi/fcoe/fcoe.c1078
-rw-r--r--drivers/scsi/fcoe/fcoe.h36
-rw-r--r--drivers/scsi/fcoe/libfcoe.c30
-rw-r--r--drivers/scsi/fnic/fnic_fcs.c2
-rw-r--r--drivers/scsi/fnic/fnic_main.c20
-rw-r--r--drivers/scsi/ibmvscsi/ibmvfc.c2
-rw-r--r--drivers/scsi/ipr.h2
-rw-r--r--drivers/scsi/iscsi_tcp.c31
-rw-r--r--drivers/scsi/libfc/fc_disc.c523
-rw-r--r--drivers/scsi/libfc/fc_elsct.c49
-rw-r--r--drivers/scsi/libfc/fc_exch.c515
-rw-r--r--drivers/scsi/libfc/fc_fcp.c31
-rw-r--r--drivers/scsi/libfc/fc_lport.c283
-rw-r--r--drivers/scsi/libfc/fc_rport.c1144
-rw-r--r--drivers/scsi/libiscsi.c201
-rw-r--r--drivers/scsi/libsrp.c1
-rw-r--r--drivers/scsi/lpfc/Makefile2
-rw-r--r--drivers/scsi/lpfc/lpfc.h19
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c10
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c904
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h18
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c2
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c2
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c259
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h4
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h74
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c134
-rw-r--r--drivers/scsi/lpfc/lpfc_mbox.c93
-rw-r--r--drivers/scsi/lpfc/lpfc_mem.c41
-rw-r--r--drivers/scsi/lpfc/lpfc_nl.h20
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c7
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c263
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h5
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h2
-rw-r--r--drivers/scsi/lpfc/lpfc_vport.c53
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c96
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.h51
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_config.c904
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_ctl.c16
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c252
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_transport.c33
-rw-r--r--drivers/scsi/pcmcia/nsp_cs.c2
-rw-r--r--drivers/scsi/pmcraid.c5604
-rw-r--r--drivers/scsi/pmcraid.h1029
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c9
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h39
-rw-r--r--drivers/scsi/qla2xxx/qla_fw.h2
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h20
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c4
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c239
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c206
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c309
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c7
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c27
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c141
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h2
-rw-r--r--drivers/scsi/qla4xxx/ql4_isr.c8
-rw-r--r--drivers/scsi/scsi.c13
-rw-r--r--drivers/scsi/scsi_error.c6
-rw-r--r--drivers/scsi/scsi_lib.c7
-rw-r--r--drivers/scsi/scsi_transport_fc.c4
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c73
-rw-r--r--drivers/scsi/scsi_transport_sas.c4
-rw-r--r--drivers/scsi/sd.c2
-rw-r--r--drivers/scsi/ses.c209
-rw-r--r--drivers/scsi/sg.c2
-rw-r--r--drivers/scsi/stex.c33
-rw-r--r--drivers/serial/Kconfig9
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/amba-pl011.c26
-rw-r--r--drivers/serial/imx.c65
-rw-r--r--drivers/serial/serial_ks8695.c6
-rw-r--r--drivers/spi/amba-pl022.c2
-rw-r--r--drivers/staging/dst/dcore.c5
-rw-r--r--drivers/staging/pohmelfs/inode.c6
-rw-r--r--drivers/video/Kconfig4
-rw-r--r--drivers/video/atmel_lcdfb.c6
-rw-r--r--drivers/video/backlight/Kconfig2
-rw-r--r--drivers/video/imxfb.c184
-rw-r--r--fs/Kconfig2
-rw-r--r--fs/block_dev.c29
-rw-r--r--fs/btrfs/extent-tree.c3
-rw-r--r--fs/btrfs/volumes.c4
-rw-r--r--fs/cifs/CHANGES5
-rw-r--r--fs/cifs/cifs_spnego.c2
-rw-r--r--fs/cifs/cifsacl.c4
-rw-r--r--fs/cifs/cifsencrypt.c1
-rw-r--r--fs/cifs/cifsfs.c22
-rw-r--r--fs/cifs/cifsfs.h2
-rw-r--r--fs/cifs/cifsglob.h21
-rw-r--r--fs/cifs/cifssmb.c316
-rw-r--r--fs/cifs/connect.c49
-rw-r--r--fs/cifs/dir.c2
-rw-r--r--fs/cifs/file.c43
-rw-r--r--fs/cifs/inode.c6
-rw-r--r--fs/cifs/transport.c17
-rw-r--r--fs/ext2/inode.c2
-rw-r--r--fs/ext3/file.c61
-rw-r--r--fs/ext4/file.c53
-rw-r--r--fs/fat/file.c22
-rw-r--r--fs/fat/misc.c4
-rw-r--r--fs/fs-writeback.c54
-rw-r--r--fs/gfs2/Makefile2
-rw-r--r--fs/gfs2/acl.c106
-rw-r--r--fs/gfs2/dentry.c18
-rw-r--r--fs/gfs2/eaops.c157
-rw-r--r--fs/gfs2/eaops.h30
-rw-r--r--fs/gfs2/export.c36
-rw-r--r--fs/gfs2/file.c1
-rw-r--r--fs/gfs2/incore.h15
-rw-r--r--fs/gfs2/inode.c159
-rw-r--r--fs/gfs2/ops_fstype.c66
-rw-r--r--fs/gfs2/ops_inode.c82
-rw-r--r--fs/gfs2/rgrp.c88
-rw-r--r--fs/gfs2/rgrp.h6
-rw-r--r--fs/gfs2/super.c46
-rw-r--r--fs/gfs2/super.h5
-rw-r--r--fs/gfs2/sys.c31
-rw-r--r--fs/gfs2/util.c41
-rw-r--r--fs/gfs2/xattr.c (renamed from fs/gfs2/eattr.c)425
-rw-r--r--fs/gfs2/xattr.h (renamed from fs/gfs2/eattr.h)54
-rw-r--r--fs/nilfs2/Kconfig2
-rw-r--r--fs/nilfs2/bmap.c151
-rw-r--r--fs/nilfs2/bmap.h76
-rw-r--r--fs/nilfs2/btree.c625
-rw-r--r--fs/nilfs2/cpfile.c11
-rw-r--r--fs/nilfs2/cpfile.h2
-rw-r--r--fs/nilfs2/dat.c42
-rw-r--r--fs/nilfs2/dat.h8
-rw-r--r--fs/nilfs2/direct.c161
-rw-r--r--fs/nilfs2/ifile.h1
-rw-r--r--fs/nilfs2/inode.c3
-rw-r--r--fs/nilfs2/ioctl.c26
-rw-r--r--fs/nilfs2/mdt.c40
-rw-r--r--fs/nilfs2/mdt.h3
-rw-r--r--fs/nilfs2/recovery.c3
-rw-r--r--fs/nilfs2/segbuf.c4
-rw-r--r--fs/nilfs2/segment.c7
-rw-r--r--fs/nilfs2/sufile.h1
-rw-r--r--fs/nilfs2/super.c100
-rw-r--r--fs/nilfs2/the_nilfs.c15
-rw-r--r--fs/nilfs2/the_nilfs.h43
-rw-r--r--fs/ntfs/file.c16
-rw-r--r--fs/ntfs/mft.c13
-rw-r--r--fs/ocfs2/file.c49
-rw-r--r--fs/partitions/check.c12
-rw-r--r--fs/splice.c30
-rw-r--r--fs/sync.c56
-rw-r--r--fs/udf/directory.c86
-rw-r--r--fs/udf/file.c2
-rw-r--r--fs/udf/inode.c19
-rw-r--r--fs/udf/lowlevel.c4
-rw-r--r--fs/udf/namei.c1
-rw-r--r--fs/xfs/linux-2.6/xfs_lrw.c3
-rw-r--r--include/asm-generic/Kbuild.asm5
-rw-r--r--include/linux/Kbuild4
-rw-r--r--include/linux/amba/bus.h5
-rw-r--r--include/linux/amba/pl093.h80
-rw-r--r--include/linux/bio.h69
-rw-r--r--include/linux/blk-iopoll.h48
-rw-r--r--include/linux/blkdev.h44
-rw-r--r--include/linux/dtlk.h19
-rw-r--r--include/linux/enclosure.h5
-rw-r--r--include/linux/fs.h22
-rw-r--r--include/linux/genhd.h21
-rw-r--r--include/linux/gfs2_ondisk.h22
-rw-r--r--include/linux/hid-debug.h48
-rw-r--r--include/linux/hid.h22
-rw-r--r--include/linux/i2c/twl4030.h19
-rw-r--r--include/linux/input/eeti_ts.h9
-rw-r--r--include/linux/input/matrix_keypad.h32
-rw-r--r--include/linux/interrupt.h1
-rw-r--r--include/linux/kvm.h127
-rw-r--r--include/linux/kvm_host.h114
-rw-r--r--include/linux/kvm_para.h1
-rw-r--r--include/linux/libps2.h1
-rw-r--r--include/linux/serio.h2
-rw-r--r--include/linux/slob_def.h5
-rw-r--r--include/linux/slub_def.h8
-rw-r--r--include/linux/tty.h4
-rw-r--r--include/linux/wm97xx.h7
-rw-r--r--include/linux/writeback.h4
-rw-r--r--include/scsi/fc/fc_gs.h1
-rw-r--r--include/scsi/fc_encode.h60
-rw-r--r--include/scsi/fc_frame.h7
-rw-r--r--include/scsi/iscsi_if.h1
-rw-r--r--include/scsi/libfc.h244
-rw-r--r--include/scsi/libiscsi.h3
-rw-r--r--include/scsi/scsi_device.h3
-rw-r--r--include/scsi/scsi_dh.h5
-rw-r--r--include/trace/events/kvm.h151
-rw-r--r--ipc/shm.c2
-rw-r--r--kernel/Makefile2
-rw-r--r--kernel/printk.c6
-rw-r--r--kernel/sysctl.c10
-rw-r--r--mm/filemap.c170
-rw-r--r--mm/hugetlb.c1
-rw-r--r--mm/slob.c5
-rw-r--r--mm/slub.c82
-rw-r--r--mm/swapfile.c6
-rwxr-xr-xscripts/extract-ikconfig14
-rw-r--r--scripts/kconfig/Makefile34
-rw-r--r--scripts/kconfig/streamline_config.pl366
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c36
-rw-r--r--sound/soc/s3c24xx/s3c24xx-ac97.h6
-rw-r--r--virt/kvm/Kconfig14
-rw-r--r--virt/kvm/coalesced_mmio.c74
-rw-r--r--virt/kvm/coalesced_mmio.h1
-rw-r--r--virt/kvm/eventfd.c578
-rw-r--r--virt/kvm/ioapic.c78
-rw-r--r--virt/kvm/iodev.h55
-rw-r--r--virt/kvm/irq_comm.c51
-rw-r--r--virt/kvm/kvm_main.c298
-rw-r--r--virt/kvm/kvm_trace.c285
943 files changed, 77070 insertions, 14743 deletions
diff --git a/Documentation/arm/Samsung-S3C24XX/CPUfreq.txt b/Documentation/arm/Samsung-S3C24XX/CPUfreq.txt
new file mode 100644
index 00000000000..76b3a11e90b
--- /dev/null
+++ b/Documentation/arm/Samsung-S3C24XX/CPUfreq.txt
@@ -0,0 +1,75 @@
+ S3C24XX CPUfreq support
+ =======================
+
+Introduction
+------------
+
+ The S3C24XX series support a number of power saving systems, such as
+ the ability to change the core, memory and peripheral operating
+ frequencies. The core control is exported via the CPUFreq driver
+ which has a number of different manual or automatic controls over the
+ rate the core is running at.
+
+ There are two forms of the driver depending on the specific CPU and
+ how the clocks are arranged. The first implementation used as single
+ PLL to feed the ARM, memory and peripherals via a series of dividers
+ and muxes and this is the implementation that is documented here. A
+ newer version where there is a seperate PLL and clock divider for the
+ ARM core is available as a seperate driver.
+
+
+Layout
+------
+
+ The code core manages the CPU specific drivers, any data that they
+ need to register and the interface to the generic drivers/cpufreq
+ system. Each CPU registers a driver to control the PLL, clock dividers
+ and anything else associated with it. Any board that wants to use this
+ framework needs to supply at least basic details of what is required.
+
+ The core registers with drivers/cpufreq at init time if all the data
+ necessary has been supplied.
+
+
+CPU support
+-----------
+
+ The support for each CPU depends on the facilities provided by the
+ SoC and the driver as each device has different PLL and clock chains
+ associated with it.
+
+
+Slow Mode
+---------
+
+ The SLOW mode where the PLL is turned off altogether and the
+ system is fed by the external crystal input is currently not
+ supported.
+
+
+sysfs
+-----
+
+ The core code exports extra information via sysfs in the directory
+ devices/system/cpu/cpu0/arch-freq.
+
+
+Board Support
+-------------
+
+ Each board that wants to use the cpufreq code must register some basic
+ information with the core driver to provide information about what the
+ board requires and any restrictions being placed on it.
+
+ The board needs to supply information about whether it needs the IO bank
+ timings changing, any maximum frequency limits and information about the
+ SDRAM refresh rate.
+
+
+
+
+Document Author
+---------------
+
+Ben Dooks, Copyright 2009 Simtec Electronics
+Licensed under GPLv2
diff --git a/Documentation/filesystems/gfs2-uevents.txt b/Documentation/filesystems/gfs2-uevents.txt
new file mode 100644
index 00000000000..fd966dc9979
--- /dev/null
+++ b/Documentation/filesystems/gfs2-uevents.txt
@@ -0,0 +1,100 @@
+ uevents and GFS2
+ ==================
+
+During the lifetime of a GFS2 mount, a number of uevents are generated.
+This document explains what the events are and what they are used
+for (by gfs_controld in gfs2-utils).
+
+A list of GFS2 uevents
+-----------------------
+
+1. ADD
+
+The ADD event occurs at mount time. It will always be the first
+uevent generated by the newly created filesystem. If the mount
+is successful, an ONLINE uevent will follow. If it is not successful
+then a REMOVE uevent will follow.
+
+The ADD uevent has two environment variables: SPECTATOR=[0|1]
+and RDONLY=[0|1] that specify the spectator status (a read-only mount
+with no journal assigned), and read-only (with journal assigned) status
+of the filesystem respectively.
+
+2. ONLINE
+
+The ONLINE uevent is generated after a successful mount or remount. It
+has the same environment variables as the ADD uevent. The ONLINE
+uevent, along with the two environment variables for spectator and
+RDONLY are a relatively recent addition (2.6.32-rc+) and will not
+be generated by older kernels.
+
+3. CHANGE
+
+The CHANGE uevent is used in two places. One is when reporting the
+successful mount of the filesystem by the first node (FIRSTMOUNT=Done).
+This is used as a signal by gfs_controld that it is then ok for other
+nodes in the cluster to mount the filesystem.
+
+The other CHANGE uevent is used to inform of the completion
+of journal recovery for one of the filesystems journals. It has
+two environment variables, JID= which specifies the journal id which
+has just been recovered, and RECOVERY=[Done|Failed] to indicate the
+success (or otherwise) of the operation. These uevents are generated
+for every journal recovered, whether it is during the initial mount
+process or as the result of gfs_controld requesting a specific journal
+recovery via the /sys/fs/gfs2/<fsname>/lock_module/recovery file.
+
+Because the CHANGE uevent was used (in early versions of gfs_controld)
+without checking the environment variables to discover the state, we
+cannot add any more functions to it without running the risk of
+someone using an older version of the user tools and breaking their
+cluster. For this reason the ONLINE uevent was used when adding a new
+uevent for a successful mount or remount.
+
+4. OFFLINE
+
+The OFFLINE uevent is only generated due to filesystem errors and is used
+as part of the "withdraw" mechanism. Currently this doesn't give any
+information about what the error is, which is something that needs to
+be fixed.
+
+5. REMOVE
+
+The REMOVE uevent is generated at the end of an unsuccessful mount
+or at the end of a umount of the filesystem. All REMOVE uevents will
+have been preceeded by at least an ADD uevent for the same fileystem,
+and unlike the other uevents is generated automatically by the kernel's
+kobject subsystem.
+
+
+Information common to all GFS2 uevents (uevent environment variables)
+----------------------------------------------------------------------
+
+1. LOCKTABLE=
+
+The LOCKTABLE is a string, as supplied on the mount command
+line (locktable=) or via fstab. It is used as a filesystem label
+as well as providing the information for a lock_dlm mount to be
+able to join the cluster.
+
+2. LOCKPROTO=
+
+The LOCKPROTO is a string, and its value depends on what is set
+on the mount command line, or via fstab. It will be either
+lock_nolock or lock_dlm. In the future other lock managers
+may be supported.
+
+3. JOURNALID=
+
+If a journal is in use by the filesystem (journals are not
+assigned for spectator mounts) then this will give the
+numeric journal id in all GFS2 uevents.
+
+4. UUID=
+
+With recent versions of gfs2-utils, mkfs.gfs2 writes a UUID
+into the filesystem superblock. If it exists, this will
+be included in every uevent relating to the filesystem.
+
+
+
diff --git a/Documentation/filesystems/seq_file.txt b/Documentation/filesystems/seq_file.txt
index b843743aa0b..0d15ebccf5b 100644
--- a/Documentation/filesystems/seq_file.txt
+++ b/Documentation/filesystems/seq_file.txt
@@ -46,7 +46,7 @@ better to do. The file is seekable, in that one can do something like the
following:
dd if=/proc/sequence of=out1 count=1
- dd if=/proc/sequence skip=1 out=out2 count=1
+ dd if=/proc/sequence skip=1 of=out2 count=1
Then concatenate the output files out1 and out2 and get the right
result. Yes, it is a thoroughly useless module, but the point is to show
diff --git a/Documentation/flexible-arrays.txt b/Documentation/flexible-arrays.txt
new file mode 100644
index 00000000000..84eb26808de
--- /dev/null
+++ b/Documentation/flexible-arrays.txt
@@ -0,0 +1,99 @@
+Using flexible arrays in the kernel
+Last updated for 2.6.31
+Jonathan Corbet <corbet@lwn.net>
+
+Large contiguous memory allocations can be unreliable in the Linux kernel.
+Kernel programmers will sometimes respond to this problem by allocating
+pages with vmalloc(). This solution not ideal, though. On 32-bit systems,
+memory from vmalloc() must be mapped into a relatively small address space;
+it's easy to run out. On SMP systems, the page table changes required by
+vmalloc() allocations can require expensive cross-processor interrupts on
+all CPUs. And, on all systems, use of space in the vmalloc() range
+increases pressure on the translation lookaside buffer (TLB), reducing the
+performance of the system.
+
+In many cases, the need for memory from vmalloc() can be eliminated by
+piecing together an array from smaller parts; the flexible array library
+exists to make this task easier.
+
+A flexible array holds an arbitrary (within limits) number of fixed-sized
+objects, accessed via an integer index. Sparse arrays are handled
+reasonably well. Only single-page allocations are made, so memory
+allocation failures should be relatively rare. The down sides are that the
+arrays cannot be indexed directly, individual object size cannot exceed the
+system page size, and putting data into a flexible array requires a copy
+operation. It's also worth noting that flexible arrays do no internal
+locking at all; if concurrent access to an array is possible, then the
+caller must arrange for appropriate mutual exclusion.
+
+The creation of a flexible array is done with:
+
+ #include <linux/flex_array.h>
+
+ struct flex_array *flex_array_alloc(int element_size,
+ unsigned int total,
+ gfp_t flags);
+
+The individual object size is provided by element_size, while total is the
+maximum number of objects which can be stored in the array. The flags
+argument is passed directly to the internal memory allocation calls. With
+the current code, using flags to ask for high memory is likely to lead to
+notably unpleasant side effects.
+
+Storing data into a flexible array is accomplished with a call to:
+
+ int flex_array_put(struct flex_array *array, unsigned int element_nr,
+ void *src, gfp_t flags);
+
+This call will copy the data from src into the array, in the position
+indicated by element_nr (which must be less than the maximum specified when
+the array was created). If any memory allocations must be performed, flags
+will be used. The return value is zero on success, a negative error code
+otherwise.
+
+There might possibly be a need to store data into a flexible array while
+running in some sort of atomic context; in this situation, sleeping in the
+memory allocator would be a bad thing. That can be avoided by using
+GFP_ATOMIC for the flags value, but, often, there is a better way. The
+trick is to ensure that any needed memory allocations are done before
+entering atomic context, using:
+
+ int flex_array_prealloc(struct flex_array *array, unsigned int start,
+ unsigned int end, gfp_t flags);
+
+This function will ensure that memory for the elements indexed in the range
+defined by start and end has been allocated. Thereafter, a
+flex_array_put() call on an element in that range is guaranteed not to
+block.
+
+Getting data back out of the array is done with:
+
+ void *flex_array_get(struct flex_array *fa, unsigned int element_nr);
+
+The return value is a pointer to the data element, or NULL if that
+particular element has never been allocated.
+
+Note that it is possible to get back a valid pointer for an element which
+has never been stored in the array. Memory for array elements is allocated
+one page at a time; a single allocation could provide memory for several
+adjacent elements. The flexible array code does not know if a specific
+element has been written; it only knows if the associated memory is
+present. So a flex_array_get() call on an element which was never stored
+in the array has the potential to return a pointer to random data. If the
+caller does not have a separate way to know which elements were actually
+stored, it might be wise, at least, to add GFP_ZERO to the flags argument
+to ensure that all elements are zeroed.
+
+There is no way to remove a single element from the array. It is possible,
+though, to remove all elements with a call to:
+
+ void flex_array_free_parts(struct flex_array *array);
+
+This call frees all elements, but leaves the array itself in place.
+Freeing the entire array is done with:
+
+ void flex_array_free(struct flex_array *array);
+
+As of this writing, there are no users of flexible arrays in the mainline
+kernel. The functions described here are also not exported to modules;
+that will probably be fixed when somebody comes up with a need for it.
diff --git a/Documentation/input/sentelic.txt b/Documentation/input/sentelic.txt
new file mode 100644
index 00000000000..f7160a2fb6a
--- /dev/null
+++ b/Documentation/input/sentelic.txt
@@ -0,0 +1,475 @@
+Copyright (C) 2002-2008 Sentelic Corporation.
+Last update: Oct-31-2008
+
+==============================================================================
+* Finger Sensing Pad Intellimouse Mode(scrolling wheel, 4th and 5th buttons)
+==============================================================================
+A) MSID 4: Scrolling wheel mode plus Forward page(4th button) and Backward
+ page (5th button)
+@1. Set sample rate to 200;
+@2. Set sample rate to 200;
+@3. Set sample rate to 80;
+@4. Issuing the "Get device ID" command (0xF2) and waits for the response;
+@5. FSP will respond 0x04.
+
+Packet 1
+ Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
+ 1 |Y|X|y|x|1|M|R|L| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 | | |B|F|W|W|W|W|
+ |---------------| |---------------| |---------------| |---------------|
+
+Byte 1: Bit7 => Y overflow
+ Bit6 => X overflow
+ Bit5 => Y sign bit
+ Bit4 => X sign bit
+ Bit3 => 1
+ Bit2 => Middle Button, 1 is pressed, 0 is not pressed.
+ Bit1 => Right Button, 1 is pressed, 0 is not pressed.
+ Bit0 => Left Button, 1 is pressed, 0 is not pressed.
+Byte 2: X Movement(9-bit 2's complement integers)
+Byte 3: Y Movement(9-bit 2's complement integers)
+Byte 4: Bit3~Bit0 => the scrolling wheel's movement since the last data report.
+ valid values, -8 ~ +7
+ Bit4 => 1 = 4th mouse button is pressed, Forward one page.
+ 0 = 4th mouse button is not pressed.
+ Bit5 => 1 = 5th mouse button is pressed, Backward one page.
+ 0 = 5th mouse button is not pressed.
+
+B) MSID 6: Horizontal and Vertical scrolling.
+@ Set bit 1 in register 0x40 to 1
+
+# FSP replaces scrolling wheel's movement as 4 bits to show horizontal and
+ vertical scrolling.
+
+Packet 1
+ Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
+ 1 |Y|X|y|x|1|M|R|L| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 | | |B|F|l|r|u|d|
+ |---------------| |---------------| |---------------| |---------------|
+
+Byte 1: Bit7 => Y overflow
+ Bit6 => X overflow
+ Bit5 => Y sign bit
+ Bit4 => X sign bit
+ Bit3 => 1
+ Bit2 => Middle Button, 1 is pressed, 0 is not pressed.
+ Bit1 => Right Button, 1 is pressed, 0 is not pressed.
+ Bit0 => Left Button, 1 is pressed, 0 is not pressed.
+Byte 2: X Movement(9-bit 2's complement integers)
+Byte 3: Y Movement(9-bit 2's complement integers)
+Byte 4: Bit0 => the Vertical scrolling movement downward.
+ Bit1 => the Vertical scrolling movement upward.
+ Bit2 => the Vertical scrolling movement rightward.
+ Bit3 => the Vertical scrolling movement leftward.
+ Bit4 => 1 = 4th mouse button is pressed, Forward one page.
+ 0 = 4th mouse button is not pressed.
+ Bit5 => 1 = 5th mouse button is pressed, Backward one page.
+ 0 = 5th mouse button is not pressed.
+
+C) MSID 7:
+# FSP uses 2 packets(8 Bytes) data to represent Absolute Position
+ so we have PACKET NUMBER to identify packets.
+ If PACKET NUMBER is 0, the packet is Packet 1.
+ If PACKET NUMBER is 1, the packet is Packet 2.
+ Please count this number in program.
+
+# MSID6 special packet will be enable at the same time when enable MSID 7.
+
+==============================================================================
+* Absolute position for STL3886-G0.
+==============================================================================
+@ Set bit 2 or 3 in register 0x40 to 1
+@ Set bit 6 in register 0x40 to 1
+
+Packet 1 (ABSOLUTE POSITION)
+ Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
+ 1 |0|1|V|1|1|M|R|L| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 |r|l|d|u|X|X|Y|Y|
+ |---------------| |---------------| |---------------| |---------------|
+
+Byte 1: Bit7~Bit6 => 00, Normal data packet
+ => 01, Absolute coordination packet
+ => 10, Notify packet
+ Bit5 => valid bit
+ Bit4 => 1
+ Bit3 => 1
+ Bit2 => Middle Button, 1 is pressed, 0 is not pressed.
+ Bit1 => Right Button, 1 is pressed, 0 is not pressed.
+ Bit0 => Left Button, 1 is pressed, 0 is not pressed.
+Byte 2: X coordinate (xpos[9:2])
+Byte 3: Y coordinate (ypos[9:2])
+Byte 4: Bit1~Bit0 => Y coordinate (xpos[1:0])
+ Bit3~Bit2 => X coordinate (ypos[1:0])
+ Bit4 => scroll up
+ Bit5 => scroll down
+ Bit6 => scroll left
+ Bit7 => scroll right
+
+Notify Packet for G0
+ Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
+ 1 |1|0|0|1|1|M|R|L| 2 |C|C|C|C|C|C|C|C| 3 |M|M|M|M|M|M|M|M| 4 |0|0|0|0|0|0|0|0|
+ |---------------| |---------------| |---------------| |---------------|
+
+Byte 1: Bit7~Bit6 => 00, Normal data packet
+ => 01, Absolute coordination packet
+ => 10, Notify packet
+ Bit5 => 0
+ Bit4 => 1
+ Bit3 => 1
+ Bit2 => Middle Button, 1 is pressed, 0 is not pressed.
+ Bit1 => Right Button, 1 is pressed, 0 is not pressed.
+ Bit0 => Left Button, 1 is pressed, 0 is not pressed.
+Byte 2: Message Type => 0x5A (Enable/Disable status packet)
+ Mode Type => 0xA5 (Normal/Icon mode status)
+Byte 3: Message Type => 0x00 (Disabled)
+ => 0x01 (Enabled)
+ Mode Type => 0x00 (Normal)
+ => 0x01 (Icon)
+Byte 4: Bit7~Bit0 => Don't Care
+
+==============================================================================
+* Absolute position for STL3888-A0.
+==============================================================================
+Packet 1 (ABSOLUTE POSITION)
+ Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
+ 1 |0|1|V|A|1|L|0|1| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 |x|x|y|y|X|X|Y|Y|
+ |---------------| |---------------| |---------------| |---------------|
+
+Byte 1: Bit7~Bit6 => 00, Normal data packet
+ => 01, Absolute coordination packet
+ => 10, Notify packet
+ Bit5 => Valid bit, 0 means that the coordinate is invalid or finger up.
+ When both fingers are up, the last two reports have zero valid
+ bit.
+ Bit4 => arc
+ Bit3 => 1
+ Bit2 => Left Button, 1 is pressed, 0 is released.
+ Bit1 => 0
+ Bit0 => 1
+Byte 2: X coordinate (xpos[9:2])
+Byte 3: Y coordinate (ypos[9:2])
+Byte 4: Bit1~Bit0 => Y coordinate (xpos[1:0])
+ Bit3~Bit2 => X coordinate (ypos[1:0])
+ Bit5~Bit4 => y1_g
+ Bit7~Bit6 => x1_g
+
+Packet 2 (ABSOLUTE POSITION)
+ Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
+ 1 |0|1|V|A|1|R|1|0| 2 |X|X|X|X|X|X|X|X| 3 |Y|Y|Y|Y|Y|Y|Y|Y| 4 |x|x|y|y|X|X|Y|Y|
+ |---------------| |---------------| |---------------| |---------------|
+
+Byte 1: Bit7~Bit6 => 00, Normal data packet
+ => 01, Absolute coordinates packet
+ => 10, Notify packet
+ Bit5 => Valid bit, 0 means that the coordinate is invalid or finger up.
+ When both fingers are up, the last two reports have zero valid
+ bit.
+ Bit4 => arc
+ Bit3 => 1
+ Bit2 => Right Button, 1 is pressed, 0 is released.
+ Bit1 => 1
+ Bit0 => 0
+Byte 2: X coordinate (xpos[9:2])
+Byte 3: Y coordinate (ypos[9:2])
+Byte 4: Bit1~Bit0 => Y coordinate (xpos[1:0])
+ Bit3~Bit2 => X coordinate (ypos[1:0])
+ Bit5~Bit4 => y2_g
+ Bit7~Bit6 => x2_g
+
+Notify Packet for STL3888-A0
+ Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
+ 1 |1|0|1|P|1|M|R|L| 2 |C|C|C|C|C|C|C|C| 3 |0|0|F|F|0|0|0|i| 4 |r|l|d|u|0|0|0|0|
+ |---------------| |---------------| |---------------| |---------------|
+
+Byte 1: Bit7~Bit6 => 00, Normal data packet
+ => 01, Absolute coordination packet
+ => 10, Notify packet
+ Bit5 => 1
+ Bit4 => when in absolute coordinates mode (valid when EN_PKT_GO is 1):
+ 0: left button is generated by the on-pad command
+ 1: left button is generated by the external button
+ Bit3 => 1
+ Bit2 => Middle Button, 1 is pressed, 0 is not pressed.
+ Bit1 => Right Button, 1 is pressed, 0 is not pressed.
+ Bit0 => Left Button, 1 is pressed, 0 is not pressed.
+Byte 2: Message Type => 0xB7 (Multi Finger, Multi Coordinate mode)
+Byte 3: Bit7~Bit6 => Don't care
+ Bit5~Bit4 => Number of fingers
+ Bit3~Bit1 => Reserved
+ Bit0 => 1: enter gesture mode; 0: leaving gesture mode
+Byte 4: Bit7 => scroll right button
+ Bit6 => scroll left button
+ Bit5 => scroll down button
+ Bit4 => scroll up button
+ * Note that if gesture and additional button (Bit4~Bit7)
+ happen at the same time, the button information will not
+ be sent.
+ Bit3~Bit0 => Reserved
+
+Sample sequence of Multi-finger, Multi-coordinate mode:
+
+ notify packet (valid bit == 1), abs pkt 1, abs pkt 2, abs pkt 1,
+ abs pkt 2, ..., notify packet(valid bit == 0)
+
+==============================================================================
+* FSP Enable/Disable packet
+==============================================================================
+ Bit 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
+BYTE |---------------|BYTE |---------------|BYTE|---------------|BYTE|---------------|
+ 1 |Y|X|0|0|1|M|R|L| 2 |0|1|0|1|1|0|1|E| 3 | | | | | | | | | 4 | | | | | | | | |
+ |---------------| |---------------| |---------------| |---------------|
+
+FSP will send out enable/disable packet when FSP receive PS/2 enable/disable
+command. Host will receive the packet which Middle, Right, Left button will
+be set. The packet only use byte 0 and byte 1 as a pattern of original packet.
+Ignore the other bytes of the packet.
+
+Byte 1: Bit7 => 0, Y overflow
+ Bit6 => 0, X overflow
+ Bit5 => 0, Y sign bit
+ Bit4 => 0, X sign bit
+ Bit3 => 1
+ Bit2 => 1, Middle Button
+ Bit1 => 1, Right Button
+ Bit0 => 1, Left Button
+Byte 2: Bit7~1 => (0101101b)
+ Bit0 => 1 = Enable
+ 0 = Disable
+Byte 3: Don't care
+Byte 4: Don't care (MOUSE ID 3, 4)
+Byte 5~8: Don't care (Absolute packet)
+
+==============================================================================
+* PS/2 Command Set
+==============================================================================
+
+FSP supports basic PS/2 commanding set and modes, refer to following URL for
+details about PS/2 commands:
+
+http://www.computer-engineering.org/index.php?title=PS/2_Mouse_Interface
+
+==============================================================================
+* Programming Sequence for Determining Packet Parsing Flow
+==============================================================================
+1. Identify FSP by reading device ID(0x00) and version(0x01) register
+
+2. Determine number of buttons by reading status2 (0x0b) register
+
+ buttons = reg[0x0b] & 0x30
+
+ if buttons == 0x30 or buttons == 0x20:
+ # two/four buttons
+ Refer to 'Finger Sensing Pad PS/2 Mouse Intellimouse'
+ section A for packet parsing detail(ignore byte 4, bit ~ 7)
+ elif buttons == 0x10:
+ # 6 buttons
+ Refer to 'Finger Sensing Pad PS/2 Mouse Intellimouse'
+ section B for packet parsing detail
+ elif buttons == 0x00:
+ # 6 buttons
+ Refer to 'Finger Sensing Pad PS/2 Mouse Intellimouse'
+ section A for packet parsing detail
+
+==============================================================================
+* Programming Sequence for Register Reading/Writing
+==============================================================================
+
+Register inversion requirement:
+
+ Following values needed to be inverted(the '~' operator in C) before being
+sent to FSP:
+
+ 0xe9, 0xee, 0xf2 and 0xff.
+
+Register swapping requirement:
+
+ Following values needed to have their higher 4 bits and lower 4 bits being
+swapped before being sent to FSP:
+
+ 10, 20, 40, 60, 80, 100 and 200.
+
+Register reading sequence:
+
+ 1. send 0xf3 PS/2 command to FSP;
+
+ 2. send 0x66 PS/2 command to FSP;
+
+ 3. send 0x88 PS/2 command to FSP;
+
+ 4. send 0xf3 PS/2 command to FSP;
+
+ 5. if the register address being to read is not required to be
+ inverted(refer to the 'Register inversion requirement' section),
+ goto step 6
+
+ 5a. send 0x68 PS/2 command to FSP;
+
+ 5b. send the inverted register address to FSP and goto step 8;
+
+ 6. if the register address being to read is not required to be
+ swapped(refer to the 'Register swapping requirement' section),
+ goto step 7
+
+ 6a. send 0xcc PS/2 command to FSP;
+
+ 6b. send the swapped register address to FSP and goto step 8;
+
+ 7. send 0x66 PS/2 command to FSP;
+
+ 7a. send the original register address to FSP and goto step 8;
+
+ 8. send 0xe9(status request) PS/2 command to FSP;
+
+ 9. the response read from FSP should be the requested register value.
+
+Register writing sequence:
+
+ 1. send 0xf3 PS/2 command to FSP;
+
+ 2. if the register address being to write is not required to be
+ inverted(refer to the 'Register inversion requirement' section),
+ goto step 3
+
+ 2a. send 0x74 PS/2 command to FSP;
+
+ 2b. send the inverted register address to FSP and goto step 5;
+
+ 3. if the register address being to write is not required to be
+ swapped(refer to the 'Register swapping requirement' section),
+ goto step 4
+
+ 3a. send 0x77 PS/2 command to FSP;
+
+ 3b. send the swapped register address to FSP and goto step 5;
+
+ 4. send 0x55 PS/2 command to FSP;
+
+ 4a. send the register address to FSP and goto step 5;
+
+ 5. send 0xf3 PS/2 command to FSP;
+
+ 6. if the register value being to write is not required to be
+ inverted(refer to the 'Register inversion requirement' section),
+ goto step 7
+
+ 6a. send 0x47 PS/2 command to FSP;
+
+ 6b. send the inverted register value to FSP and goto step 9;
+
+ 7. if the register value being to write is not required to be
+ swapped(refer to the 'Register swapping requirement' section),
+ goto step 8
+
+ 7a. send 0x44 PS/2 command to FSP;
+
+ 7b. send the swapped register value to FSP and goto step 9;
+
+ 8. send 0x33 PS/2 command to FSP;
+
+ 8a. send the register value to FSP;
+
+ 9. the register writing sequence is completed.
+
+==============================================================================
+* Register Listing
+==============================================================================
+
+offset width default r/w name
+0x00 bit7~bit0 0x01 RO device ID
+
+0x01 bit7~bit0 0xc0 RW version ID
+
+0x02 bit7~bit0 0x01 RO vendor ID
+
+0x03 bit7~bit0 0x01 RO product ID
+
+0x04 bit3~bit0 0x01 RW revision ID
+
+0x0b RO test mode status 1
+ bit3 1 RO 0: rotate 180 degree, 1: no rotation
+
+ bit5~bit4 RO number of buttons
+ 11 => 2, lbtn/rbtn
+ 10 => 4, lbtn/rbtn/scru/scrd
+ 01 => 6, lbtn/rbtn/scru/scrd/scrl/scrr
+ 00 => 6, lbtn/rbtn/scru/scrd/fbtn/bbtn
+
+0x0f RW register file page control
+ bit0 0 RW 1 to enable page 1 register files
+
+0x10 RW system control 1
+ bit0 1 RW Reserved, must be 1
+ bit1 0 RW Reserved, must be 0
+ bit4 1 RW Reserved, must be 0
+ bit5 0 RW register clock gating enable
+ 0: read only, 1: read/write enable
+ (Note that following registers does not require clock gating being
+ enabled prior to write: 05 06 07 08 09 0c 0f 10 11 12 16 17 18 23 2e
+ 40 41 42 43.)
+
+0x31 RW on-pad command detection
+ bit7 0 RW on-pad command left button down tag
+ enable
+ 0: disable, 1: enable
+
+0x34 RW on-pad command control 5
+ bit4~bit0 0x05 RW XLO in 0s/4/1, so 03h = 0010.1b = 2.5
+ (Note that position unit is in 0.5 scanline)
+
+ bit7 0 RW on-pad tap zone enable
+ 0: disable, 1: enable
+
+0x35 RW on-pad command control 6
+ bit4~bit0 0x1d RW XHI in 0s/4/1, so 19h = 1100.1b = 12.5
+ (Note that position unit is in 0.5 scanline)
+
+0x36 RW on-pad command control 7
+ bit4~bit0 0x04 RW YLO in 0s/4/1, so 03h = 0010.1b = 2.5
+ (Note that position unit is in 0.5 scanline)
+
+0x37 RW on-pad command control 8
+ bit4~bit0 0x13 RW YHI in 0s/4/1, so 11h = 1000.1b = 8.5
+ (Note that position unit is in 0.5 scanline)
+
+0x40 RW system control 5
+ bit1 0 RW FSP Intellimouse mode enable
+ 0: disable, 1: enable
+
+ bit2 0 RW movement + abs. coordinate mode enable
+ 0: disable, 1: enable
+ (Note that this function has the functionality of bit 1 even when
+ bit 1 is not set. However, the format is different from that of bit 1.
+ In addition, when bit 1 and bit 2 are set at the same time, bit 2 will
+ override bit 1.)
+
+ bit3 0 RW abs. coordinate only mode enable
+ 0: disable, 1: enable
+ (Note that this function has the functionality of bit 1 even when
+ bit 1 is not set. However, the format is different from that of bit 1.
+ In addition, when bit 1, bit 2 and bit 3 are set at the same time,
+ bit 3 will override bit 1 and 2.)
+
+ bit5 0 RW auto switch enable
+ 0: disable, 1: enable
+
+ bit6 0 RW G0 abs. + notify packet format enable
+ 0: disable, 1: enable
+ (Note that the absolute/relative coordinate output still depends on
+ bit 2 and 3. That is, if any of those bit is 1, host will receive
+ absolute coordinates; otherwise, host only receives packets with
+ relative coordinate.)
+
+0x43 RW on-pad control
+ bit0 0 RW on-pad control enable
+ 0: disable, 1: enable
+ (Note that if this bit is cleared, bit 3/5 will be ineffective)
+
+ bit3 0 RW on-pad fix vertical scrolling enable
+ 0: disable, 1: enable
+
+ bit5 0 RW on-pad fix horizontal scrolling enable
+ 0: disable, 1: enable
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 1c058b552e9..aafca0a8f66 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -193,7 +193,7 @@ Code Seq# Include File Comments
0xAD 00 Netfilter device in development:
<mailto:rusty@rustcorp.com.au>
0xAE all linux/kvm.h Kernel-based Virtual Machine
- <mailto:kvm-devel@lists.sourceforge.net>
+ <mailto:kvm@vger.kernel.org>
0xB0 all RATIO devices in development:
<mailto:vgo@ratio.de>
0xB1 00-1F PPPoX <mailto:mostrows@styx.uwaterloo.ca>
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index cb3a169e372..3a238644c81 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -57,6 +57,7 @@ parameter is applicable:
ISAPNP ISA PnP code is enabled.
ISDN Appropriate ISDN support is enabled.
JOY Appropriate joystick support is enabled.
+ KVM Kernel Virtual Machine support is enabled.
LIBATA Libata driver is enabled
LP Printer support is enabled.
LOOP Loopback device support is enabled.
@@ -1098,6 +1099,44 @@ and is between 256 and 4096 characters. It is defined in the file
kstack=N [X86] Print N words from the kernel stack
in oops dumps.
+ kvm.ignore_msrs=[KVM] Ignore guest accesses to unhandled MSRs.
+ Default is 0 (don't ignore, but inject #GP)
+
+ kvm.oos_shadow= [KVM] Disable out-of-sync shadow paging.
+ Default is 1 (enabled)
+
+ kvm-amd.nested= [KVM,AMD] Allow nested virtualization in KVM/SVM.
+ Default is 0 (off)
+
+ kvm-amd.npt= [KVM,AMD] Disable nested paging (virtualized MMU)
+ for all guests.
+ Default is 1 (enabled) if in 64bit or 32bit-PAE mode
+
+ kvm-intel.bypass_guest_pf=
+ [KVM,Intel] Disables bypassing of guest page faults
+ on Intel chips. Default is 1 (enabled)
+
+ kvm-intel.ept= [KVM,Intel] Disable extended page tables
+ (virtualized MMU) support on capable Intel chips.
+ Default is 1 (enabled)
+
+ kvm-intel.emulate_invalid_guest_state=
+ [KVM,Intel] Enable emulation of invalid guest states
+ Default is 0 (disabled)
+
+ kvm-intel.flexpriority=
+ [KVM,Intel] Disable FlexPriority feature (TPR shadow).
+ Default is 1 (enabled)
+
+ kvm-intel.unrestricted_guest=
+ [KVM,Intel] Disable unrestricted guest feature
+ (virtualized real and unpaged mode) on capable
+ Intel chips. Default is 1 (enabled)
+
+ kvm-intel.vpid= [KVM,Intel] Disable Virtual Processor Identification
+ feature (tagged TLBs) on capable Intel chips.
+ Default is 1 (enabled)
+
l2cr= [PPC]
l3cr= [PPC]
diff --git a/Documentation/kvm/api.txt b/Documentation/kvm/api.txt
new file mode 100644
index 00000000000..5a4bc8cf6d0
--- /dev/null
+++ b/Documentation/kvm/api.txt
@@ -0,0 +1,759 @@
+The Definitive KVM (Kernel-based Virtual Machine) API Documentation
+===================================================================
+
+1. General description
+
+The kvm API is a set of ioctls that are issued to control various aspects
+of a virtual machine. The ioctls belong to three classes
+
+ - System ioctls: These query and set global attributes which affect the
+ whole kvm subsystem. In addition a system ioctl is used to create
+ virtual machines
+
+ - VM ioctls: These query and set attributes that affect an entire virtual
+ machine, for example memory layout. In addition a VM ioctl is used to
+ create virtual cpus (vcpus).
+
+ Only run VM ioctls from the same process (address space) that was used
+ to create the VM.
+
+ - vcpu ioctls: These query and set attributes that control the operation
+ of a single virtual cpu.
+
+ Only run vcpu ioctls from the same thread that was used to create the
+ vcpu.
+
+2. File descritpors
+
+The kvm API is centered around file descriptors. An initial
+open("/dev/kvm") obtains a handle to the kvm subsystem; this handle
+can be used to issue system ioctls. A KVM_CREATE_VM ioctl on this
+handle will create a VM file descripror which can be used to issue VM
+ioctls. A KVM_CREATE_VCPU ioctl on a VM fd will create a virtual cpu
+and return a file descriptor pointing to it. Finally, ioctls on a vcpu
+fd can be used to control the vcpu, including the important task of
+actually running guest code.
+
+In general file descriptors can be migrated among processes by means
+of fork() and the SCM_RIGHTS facility of unix domain socket. These
+kinds of tricks are explicitly not supported by kvm. While they will
+not cause harm to the host, their actual behavior is not guaranteed by
+the API. The only supported use is one virtual machine per process,
+and one vcpu per thread.
+
+3. Extensions
+
+As of Linux 2.6.22, the KVM ABI has been stabilized: no backward
+incompatible change are allowed. However, there is an extension
+facility that allows backward-compatible extensions to the API to be
+queried and used.
+
+The extension mechanism is not based on on the Linux version number.
+Instead, kvm defines extension identifiers and a facility to query
+whether a particular extension identifier is available. If it is, a
+set of ioctls is available for application use.
+
+4. API description
+
+This section describes ioctls that can be used to control kvm guests.
+For each ioctl, the following information is provided along with a
+description:
+
+ Capability: which KVM extension provides this ioctl. Can be 'basic',
+ which means that is will be provided by any kernel that supports
+ API version 12 (see section 4.1), or a KVM_CAP_xyz constant, which
+ means availability needs to be checked with KVM_CHECK_EXTENSION
+ (see section 4.4).
+
+ Architectures: which instruction set architectures provide this ioctl.
+ x86 includes both i386 and x86_64.
+
+ Type: system, vm, or vcpu.
+
+ Parameters: what parameters are accepted by the ioctl.
+
+ Returns: the return value. General error numbers (EBADF, ENOMEM, EINVAL)
+ are not detailed, but errors with specific meanings are.
+
+4.1 KVM_GET_API_VERSION
+
+Capability: basic
+Architectures: all
+Type: system ioctl
+Parameters: none
+Returns: the constant KVM_API_VERSION (=12)
+
+This identifies the API version as the stable kvm API. It is not
+expected that this number will change. However, Linux 2.6.20 and
+2.6.21 report earlier versions; these are not documented and not
+supported. Applications should refuse to run if KVM_GET_API_VERSION
+returns a value other than 12. If this check passes, all ioctls
+described as 'basic' will be available.
+
+4.2 KVM_CREATE_VM
+
+Capability: basic
+Architectures: all
+Type: system ioctl
+Parameters: none
+Returns: a VM fd that can be used to control the new virtual machine.
+
+The new VM has no virtual cpus and no memory. An mmap() of a VM fd
+will access the virtual machine's physical address space; offset zero
+corresponds to guest physical address zero. Use of mmap() on a VM fd
+is discouraged if userspace memory allocation (KVM_CAP_USER_MEMORY) is
+available.
+
+4.3 KVM_GET_MSR_INDEX_LIST
+
+Capability: basic
+Architectures: x86
+Type: system
+Parameters: struct kvm_msr_list (in/out)
+Returns: 0 on success; -1 on error
+Errors:
+ E2BIG: the msr index list is to be to fit in the array specified by
+ the user.
+
+struct kvm_msr_list {
+ __u32 nmsrs; /* number of msrs in entries */
+ __u32 indices[0];
+};
+
+This ioctl returns the guest msrs that are supported. The list varies
+by kvm version and host processor, but does not change otherwise. The
+user fills in the size of the indices array in nmsrs, and in return
+kvm adjusts nmsrs to reflect the actual number of msrs and fills in
+the indices array with their numbers.
+
+4.4 KVM_CHECK_EXTENSION
+
+Capability: basic
+Architectures: all
+Type: system ioctl
+Parameters: extension identifier (KVM_CAP_*)
+Returns: 0 if unsupported; 1 (or some other positive integer) if supported
+
+The API allows the application to query about extensions to the core
+kvm API. Userspace passes an extension identifier (an integer) and
+receives an integer that describes the extension availability.
+Generally 0 means no and 1 means yes, but some extensions may report
+additional information in the integer return value.
+
+4.5 KVM_GET_VCPU_MMAP_SIZE
+
+Capability: basic
+Architectures: all
+Type: system ioctl
+Parameters: none
+Returns: size of vcpu mmap area, in bytes
+
+The KVM_RUN ioctl (cf.) communicates with userspace via a shared
+memory region. This ioctl returns the size of that region. See the
+KVM_RUN documentation for details.
+
+4.6 KVM_SET_MEMORY_REGION
+
+Capability: basic
+Architectures: all
+Type: vm ioctl
+Parameters: struct kvm_memory_region (in)
+Returns: 0 on success, -1 on error
+
+struct kvm_memory_region {
+ __u32 slot;
+ __u32 flags;
+ __u64 guest_phys_addr;
+ __u64 memory_size; /* bytes */
+};
+
+/* for kvm_memory_region::flags */
+#define KVM_MEM_LOG_DIRTY_PAGES 1UL
+
+This ioctl allows the user to create or modify a guest physical memory
+slot. When changing an existing slot, it may be moved in the guest
+physical memory space, or its flags may be modified. It may not be
+resized. Slots may not overlap.
+
+The flags field supports just one flag, KVM_MEM_LOG_DIRTY_PAGES, which
+instructs kvm to keep track of writes to memory within the slot. See
+the KVM_GET_DIRTY_LOG ioctl.
+
+It is recommended to use the KVM_SET_USER_MEMORY_REGION ioctl instead
+of this API, if available. This newer API allows placing guest memory
+at specified locations in the host address space, yielding better
+control and easy access.
+
+4.6 KVM_CREATE_VCPU
+
+Capability: basic
+Architectures: all
+Type: vm ioctl
+Parameters: vcpu id (apic id on x86)
+Returns: vcpu fd on success, -1 on error
+
+This API adds a vcpu to a virtual machine. The vcpu id is a small integer
+in the range [0, max_vcpus).
+
+4.7 KVM_GET_DIRTY_LOG (vm ioctl)
+
+Capability: basic
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_dirty_log (in/out)
+Returns: 0 on success, -1 on error
+
+/* for KVM_GET_DIRTY_LOG */
+struct kvm_dirty_log {
+ __u32 slot;
+ __u32 padding;
+ union {
+ void __user *dirty_bitmap; /* one bit per page */
+ __u64 padding;
+ };
+};
+
+Given a memory slot, return a bitmap containing any pages dirtied
+since the last call to this ioctl. Bit 0 is the first page in the
+memory slot. Ensure the entire structure is cleared to avoid padding
+issues.
+
+4.8 KVM_SET_MEMORY_ALIAS
+
+Capability: basic
+Architectures: x86
+Type: vm ioctl
+Parameters: struct kvm_memory_alias (in)
+Returns: 0 (success), -1 (error)
+
+struct kvm_memory_alias {
+ __u32 slot; /* this has a different namespace than memory slots */
+ __u32 flags;
+ __u64 guest_phys_addr;
+ __u64 memory_size;
+ __u64 target_phys_addr;
+};
+
+Defines a guest physical address space region as an alias to another
+region. Useful for aliased address, for example the VGA low memory
+window. Should not be used with userspace memory.
+
+4.9 KVM_RUN
+
+Capability: basic
+Architectures: all
+Type: vcpu ioctl
+Parameters: none
+Returns: 0 on success, -1 on error
+Errors:
+ EINTR: an unmasked signal is pending
+
+This ioctl is used to run a guest virtual cpu. While there are no
+explicit parameters, there is an implicit parameter block that can be
+obtained by mmap()ing the vcpu fd at offset 0, with the size given by
+KVM_GET_VCPU_MMAP_SIZE. The parameter block is formatted as a 'struct
+kvm_run' (see below).
+
+4.10 KVM_GET_REGS
+
+Capability: basic
+Architectures: all
+Type: vcpu ioctl
+Parameters: struct kvm_regs (out)
+Returns: 0 on success, -1 on error
+
+Reads the general purpose registers from the vcpu.
+
+/* x86 */
+struct kvm_regs {
+ /* out (KVM_GET_REGS) / in (KVM_SET_REGS) */
+ __u64 rax, rbx, rcx, rdx;
+ __u64 rsi, rdi, rsp, rbp;
+ __u64 r8, r9, r10, r11;
+ __u64 r12, r13, r14, r15;
+ __u64 rip, rflags;
+};
+
+4.11 KVM_SET_REGS
+
+Capability: basic
+Architectures: all
+Type: vcpu ioctl
+Parameters: struct kvm_regs (in)
+Returns: 0 on success, -1 on error
+
+Writes the general purpose registers into the vcpu.
+
+See KVM_GET_REGS for the data structure.
+
+4.12 KVM_GET_SREGS
+
+Capability: basic
+Architectures: x86
+Type: vcpu ioctl
+Parameters: struct kvm_sregs (out)
+Returns: 0 on success, -1 on error
+
+Reads special registers from the vcpu.
+
+/* x86 */
+struct kvm_sregs {
+ struct kvm_segment cs, ds, es, fs, gs, ss;
+ struct kvm_segment tr, ldt;
+ struct kvm_dtable gdt, idt;
+ __u64 cr0, cr2, cr3, cr4, cr8;
+ __u64 efer;
+ __u64 apic_base;
+ __u64 interrupt_bitmap[(KVM_NR_INTERRUPTS + 63) / 64];
+};
+
+interrupt_bitmap is a bitmap of pending external interrupts. At most
+one bit may be set. This interrupt has been acknowledged by the APIC
+but not yet injected into the cpu core.
+
+4.13 KVM_SET_SREGS
+
+Capability: basic
+Architectures: x86
+Type: vcpu ioctl
+Parameters: struct kvm_sregs (in)
+Returns: 0 on success, -1 on error
+
+Writes special registers into the vcpu. See KVM_GET_SREGS for the
+data structures.
+
+4.14 KVM_TRANSLATE
+
+Capability: basic
+Architectures: x86
+Type: vcpu ioctl
+Parameters: struct kvm_translation (in/out)
+Returns: 0 on success, -1 on error
+
+Translates a virtual address according to the vcpu's current address
+translation mode.
+
+struct kvm_translation {
+ /* in */
+ __u64 linear_address;
+
+ /* out */
+ __u64 physical_address;
+ __u8 valid;
+ __u8 writeable;
+ __u8 usermode;
+ __u8 pad[5];
+};
+
+4.15 KVM_INTERRUPT
+
+Capability: basic
+Architectures: x86
+Type: vcpu ioctl
+Parameters: struct kvm_interrupt (in)
+Returns: 0 on success, -1 on error
+
+Queues a hardware interrupt vector to be injected. This is only
+useful if in-kernel local APIC is not used.
+
+/* for KVM_INTERRUPT */
+struct kvm_interrupt {
+ /* in */
+ __u32 irq;
+};
+
+Note 'irq' is an interrupt vector, not an interrupt pin or line.
+
+4.16 KVM_DEBUG_GUEST
+
+Capability: basic
+Architectures: none
+Type: vcpu ioctl
+Parameters: none)
+Returns: -1 on error
+
+Support for this has been removed. Use KVM_SET_GUEST_DEBUG instead.
+
+4.17 KVM_GET_MSRS
+
+Capability: basic
+Architectures: x86
+Type: vcpu ioctl
+Parameters: struct kvm_msrs (in/out)
+Returns: 0 on success, -1 on error
+
+Reads model-specific registers from the vcpu. Supported msr indices can
+be obtained using KVM_GET_MSR_INDEX_LIST.
+
+struct kvm_msrs {
+ __u32 nmsrs; /* number of msrs in entries */
+ __u32 pad;
+
+ struct kvm_msr_entry entries[0];
+};
+
+struct kvm_msr_entry {
+ __u32 index;
+ __u32 reserved;
+ __u64 data;
+};
+
+Application code should set the 'nmsrs' member (which indicates the
+size of the entries array) and the 'index' member of each array entry.
+kvm will fill in the 'data' member.
+
+4.18 KVM_SET_MSRS
+
+Capability: basic
+Architectures: x86
+Type: vcpu ioctl
+Parameters: struct kvm_msrs (in)
+Returns: 0 on success, -1 on error
+
+Writes model-specific registers to the vcpu. See KVM_GET_MSRS for the
+data structures.
+
+Application code should set the 'nmsrs' member (which indicates the
+size of the entries array), and the 'index' and 'data' members of each
+array entry.
+
+4.19 KVM_SET_CPUID
+
+Capability: basic
+Architectures: x86
+Type: vcpu ioctl
+Parameters: struct kvm_cpuid (in)
+Returns: 0 on success, -1 on error
+
+Defines the vcpu responses to the cpuid instruction. Applications
+should use the KVM_SET_CPUID2 ioctl if available.
+
+
+struct kvm_cpuid_entry {
+ __u32 function;
+ __u32 eax;
+ __u32 ebx;
+ __u32 ecx;
+ __u32 edx;
+ __u32 padding;
+};
+
+/* for KVM_SET_CPUID */
+struct kvm_cpuid {
+ __u32 nent;
+ __u32 padding;
+ struct kvm_cpuid_entry entries[0];
+};
+
+4.20 KVM_SET_SIGNAL_MASK
+
+Capability: basic
+Architectures: x86
+Type: vcpu ioctl
+Parameters: struct kvm_signal_mask (in)
+Returns: 0 on success, -1 on error
+
+Defines which signals are blocked during execution of KVM_RUN. This
+signal mask temporarily overrides the threads signal mask. Any
+unblocked signal received (except SIGKILL and SIGSTOP, which retain
+their traditional behaviour) will cause KVM_RUN to return with -EINTR.
+
+Note the signal will only be delivered if not blocked by the original
+signal mask.
+
+/* for KVM_SET_SIGNAL_MASK */
+struct kvm_signal_mask {
+ __u32 len;
+ __u8 sigset[0];
+};
+
+4.21 KVM_GET_FPU
+
+Capability: basic
+Architectures: x86
+Type: vcpu ioctl
+Parameters: struct kvm_fpu (out)
+Returns: 0 on success, -1 on error
+
+Reads the floating point state from the vcpu.
+
+/* for KVM_GET_FPU and KVM_SET_FPU */
+struct kvm_fpu {
+ __u8 fpr[8][16];
+ __u16 fcw;
+ __u16 fsw;
+ __u8 ftwx; /* in fxsave format */
+ __u8 pad1;
+ __u16 last_opcode;
+ __u64 last_ip;
+ __u64 last_dp;
+ __u8 xmm[16][16];
+ __u32 mxcsr;
+ __u32 pad2;
+};
+
+4.22 KVM_SET_FPU
+
+Capability: basic
+Architectures: x86
+Type: vcpu ioctl
+Parameters: struct kvm_fpu (in)
+Returns: 0 on success, -1 on error
+
+Writes the floating point state to the vcpu.
+
+/* for KVM_GET_FPU and KVM_SET_FPU */
+struct kvm_fpu {
+ __u8 fpr[8][16];
+ __u16 fcw;
+ __u16 fsw;
+ __u8 ftwx; /* in fxsave format */
+ __u8 pad1;
+ __u16 last_opcode;
+ __u64 last_ip;
+ __u64 last_dp;
+ __u8 xmm[16][16];
+ __u32 mxcsr;
+ __u32 pad2;
+};
+
+4.23 KVM_CREATE_IRQCHIP
+
+Capability: KVM_CAP_IRQCHIP
+Architectures: x86, ia64
+Type: vm ioctl
+Parameters: none
+Returns: 0 on success, -1 on error
+
+Creates an interrupt controller model in the kernel. On x86, creates a virtual
+ioapic, a virtual PIC (two PICs, nested), and sets up future vcpus to have a
+local APIC. IRQ routing for GSIs 0-15 is set to both PIC and IOAPIC; GSI 16-23
+only go to the IOAPIC. On ia64, a IOSAPIC is created.
+
+4.24 KVM_IRQ_LINE
+
+Capability: KVM_CAP_IRQCHIP
+Architectures: x86, ia64
+Type: vm ioctl
+Parameters: struct kvm_irq_level
+Returns: 0 on success, -1 on error
+
+Sets the level of a GSI input to the interrupt controller model in the kernel.
+Requires that an interrupt controller model has been previously created with
+KVM_CREATE_IRQCHIP. Note that edge-triggered interrupts require the level
+to be set to 1 and then back to 0.
+
+struct kvm_irq_level {
+ union {
+ __u32 irq; /* GSI */
+ __s32 status; /* not used for KVM_IRQ_LEVEL */
+ };
+ __u32 level; /* 0 or 1 */
+};
+
+4.25 KVM_GET_IRQCHIP
+
+Capability: KVM_CAP_IRQCHIP
+Architectures: x86, ia64
+Type: vm ioctl
+Parameters: struct kvm_irqchip (in/out)
+Returns: 0 on success, -1 on error
+
+Reads the state of a kernel interrupt controller created with
+KVM_CREATE_IRQCHIP into a buffer provided by the caller.
+
+struct kvm_irqchip {
+ __u32 chip_id; /* 0 = PIC1, 1 = PIC2, 2 = IOAPIC */
+ __u32 pad;
+ union {
+ char dummy[512]; /* reserving space */
+ struct kvm_pic_state pic;
+ struct kvm_ioapic_state ioapic;
+ } chip;
+};
+
+4.26 KVM_SET_IRQCHIP
+
+Capability: KVM_CAP_IRQCHIP
+Architectures: x86, ia64
+Type: vm ioctl
+Parameters: struct kvm_irqchip (in)
+Returns: 0 on success, -1 on error
+
+Sets the state of a kernel interrupt controller created with
+KVM_CREATE_IRQCHIP from a buffer provided by the caller.
+
+struct kvm_irqchip {
+ __u32 chip_id; /* 0 = PIC1, 1 = PIC2, 2 = IOAPIC */
+ __u32 pad;
+ union {
+ char dummy[512]; /* reserving space */
+ struct kvm_pic_state pic;
+ struct kvm_ioapic_state ioapic;
+ } chip;
+};
+
+5. The kvm_run structure
+
+Application code obtains a pointer to the kvm_run structure by
+mmap()ing a vcpu fd. From that point, application code can control
+execution by changing fields in kvm_run prior to calling the KVM_RUN
+ioctl, and obtain information about the reason KVM_RUN returned by
+looking up structure members.
+
+struct kvm_run {
+ /* in */
+ __u8 request_interrupt_window;
+
+Request that KVM_RUN return when it becomes possible to inject external
+interrupts into the guest. Useful in conjunction with KVM_INTERRUPT.
+
+ __u8 padding1[7];
+
+ /* out */
+ __u32 exit_reason;
+
+When KVM_RUN has returned successfully (return value 0), this informs
+application code why KVM_RUN has returned. Allowable values for this
+field are detailed below.
+
+ __u8 ready_for_interrupt_injection;
+
+If request_interrupt_window has been specified, this field indicates
+an interrupt can be injected now with KVM_INTERRUPT.
+
+ __u8 if_flag;
+
+The value of the current interrupt flag. Only valid if in-kernel
+local APIC is not used.
+
+ __u8 padding2[2];
+
+ /* in (pre_kvm_run), out (post_kvm_run) */
+ __u64 cr8;
+
+The value of the cr8 register. Only valid if in-kernel local APIC is
+not used. Both input and output.
+
+ __u64 apic_base;
+
+The value of the APIC BASE msr. Only valid if in-kernel local
+APIC is not used. Both input and output.
+
+ union {
+ /* KVM_EXIT_UNKNOWN */
+ struct {
+ __u64 hardware_exit_reason;
+ } hw;
+
+If exit_reason is KVM_EXIT_UNKNOWN, the vcpu has exited due to unknown
+reasons. Further architecture-specific information is available in
+hardware_exit_reason.
+
+ /* KVM_EXIT_FAIL_ENTRY */
+ struct {
+ __u64 hardware_entry_failure_reason;
+ } fail_entry;
+
+If exit_reason is KVM_EXIT_FAIL_ENTRY, the vcpu could not be run due
+to unknown reasons. Further architecture-specific information is
+available in hardware_entry_failure_reason.
+
+ /* KVM_EXIT_EXCEPTION */
+ struct {
+ __u32 exception;
+ __u32 error_code;
+ } ex;
+
+Unused.
+
+ /* KVM_EXIT_IO */
+ struct {
+#define KVM_EXIT_IO_IN 0
+#define KVM_EXIT_IO_OUT 1
+ __u8 direction;
+ __u8 size; /* bytes */
+ __u16 port;
+ __u32 count;
+ __u64 data_offset; /* relative to kvm_run start */
+ } io;
+
+If exit_reason is KVM_EXIT_IO_IN or KVM_EXIT_IO_OUT, then the vcpu has
+executed a port I/O instruction which could not be satisfied by kvm.
+data_offset describes where the data is located (KVM_EXIT_IO_OUT) or
+where kvm expects application code to place the data for the next
+KVM_RUN invocation (KVM_EXIT_IO_IN). Data format is a patcked array.
+
+ struct {
+ struct kvm_debug_exit_arch arch;
+ } debug;
+
+Unused.
+
+ /* KVM_EXIT_MMIO */
+ struct {
+ __u64 phys_addr;
+ __u8 data[8];
+ __u32 len;
+ __u8 is_write;
+ } mmio;
+
+If exit_reason is KVM_EXIT_MMIO or KVM_EXIT_IO_OUT, then the vcpu has
+executed a memory-mapped I/O instruction which could not be satisfied
+by kvm. The 'data' member contains the written data if 'is_write' is
+true, and should be filled by application code otherwise.
+
+ /* KVM_EXIT_HYPERCALL */
+ struct {
+ __u64 nr;
+ __u64 args[6];
+ __u64 ret;
+ __u32 longmode;
+ __u32 pad;
+ } hypercall;
+
+Unused.
+
+ /* KVM_EXIT_TPR_ACCESS */
+ struct {
+ __u64 rip;
+ __u32 is_write;
+ __u32 pad;
+ } tpr_access;
+
+To be documented (KVM_TPR_ACCESS_REPORTING).
+
+ /* KVM_EXIT_S390_SIEIC */
+ struct {
+ __u8 icptcode;
+ __u64 mask; /* psw upper half */
+ __u64 addr; /* psw lower half */
+ __u16 ipa;
+ __u32 ipb;
+ } s390_sieic;
+
+s390 specific.
+
+ /* KVM_EXIT_S390_RESET */
+#define KVM_S390_RESET_POR 1
+#define KVM_S390_RESET_CLEAR 2
+#define KVM_S390_RESET_SUBSYSTEM 4
+#define KVM_S390_RESET_CPU_INIT 8
+#define KVM_S390_RESET_IPL 16
+ __u64 s390_reset_flags;
+
+s390 specific.
+
+ /* KVM_EXIT_DCR */
+ struct {
+ __u32 dcrn;
+ __u32 data;
+ __u8 is_write;
+ } dcr;
+
+powerpc specific.
+
+ /* Fix the size of the union. */
+ char padding[256];
+ };
+};
diff --git a/Documentation/vm/slub.txt b/Documentation/vm/slub.txt
index bb1f5c6e28b..510917ff59e 100644
--- a/Documentation/vm/slub.txt
+++ b/Documentation/vm/slub.txt
@@ -41,6 +41,8 @@ Possible debug options are
P Poisoning (object and padding)
U User tracking (free and alloc)
T Trace (please only use on single slabs)
+ O Switch debugging off for caches that would have
+ caused higher minimum slab orders
- Switch all debugging off (useful if the kernel is
configured with CONFIG_SLUB_DEBUG_ON)
@@ -59,6 +61,14 @@ to the dentry cache with
slub_debug=F,dentry
+Debugging options may require the minimum possible slab order to increase as
+a result of storing the metadata (for example, caches with PAGE_SIZE object
+sizes). This has a higher liklihood of resulting in slab allocation errors
+in low memory situations or if there's high fragmentation of memory. To
+switch off debugging for such caches by default, use
+
+ slub_debug=O
+
In case you forgot to enable debugging on the kernel command line: It is
possible to enable debugging manually when the kernel is up. Look at the
contents of:
diff --git a/MAINTAINERS b/MAINTAINERS
index e95cb772f93..837b5985ac4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -534,10 +534,30 @@ L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only)
W: http://maxim.org.za/at91_26.html
S: Maintained
+ARM/BCMRING ARM ARCHITECTURE
+M: Leo Chen <leochen@broadcom.com>
+M: Scott Branden <sbranden@broadcom.com>
+L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only)
+S: Maintained
+F: arch/arm/mach-bcmring
+
+ARM/BCMRING MTD NAND DRIVER
+M: Leo Chen <leochen@broadcom.com>
+M: Scott Branden <sbranden@broadcom.com>
+L: linux-mtd@lists.infradead.org
+S: Maintained
+F: drivers/mtd/nand/bcm_umi_nand.c
+F: drivers/mtd/nand/bcm_umi_bch.c
+F: drivers/mtd/nand/bcm_umi_hamming.c
+F: drivers/mtd/nand/nand_bcm_umi.h
+
ARM/CIRRUS LOGIC EP93XX ARM ARCHITECTURE
-M: Lennert Buytenhek <kernel@wantstofly.org>
+M: Hartley Sweeten <hsweeten@visionengravers.com>
+M: Ryan Mallon <ryan@bluewatersys.com>
L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only)
S: Maintained
+F: arch/arm/mach-ep93xx/
+F: arch/arm/mach-ep93xx/include/mach/
ARM/CIRRUS LOGIC EDB9315A MACHINE SUPPORT
M: Lennert Buytenhek <kernel@wantstofly.org>
@@ -685,6 +705,18 @@ ARM/MAGICIAN MACHINE SUPPORT
M: Philipp Zabel <philipp.zabel@gmail.com>
S: Maintained
+ARM/Marvell Loki/Kirkwood/MV78xx0/Orion SOC support
+M: Lennert Buytenhek <buytenh@marvell.com>
+M: Nicolas Pitre <nico@marvell.com>
+L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only)
+T: git git://git.marvell.com/orion
+S: Maintained
+F: arch/arm/mach-loki/
+F: arch/arm/mach-kirkwood/
+F: arch/arm/mach-mv78xx0/
+F: arch/arm/mach-orion5x/
+F: arch/arm/plat-orion/
+
ARM/MIOA701 MACHINE SUPPORT
M: Robert Jarzmik <robert.jarzmik@free.fr>
L: linux-arm-kernel@lists.arm.linux.org.uk (subscribers-only)
@@ -2926,6 +2958,7 @@ F: include/linux/sunrpc/
KERNEL VIRTUAL MACHINE (KVM)
M: Avi Kivity <avi@redhat.com>
+M: Marcelo Tosatti <mtosatti@redhat.com>
L: kvm@vger.kernel.org
W: http://kvm.qumranet.com
S: Supported
@@ -3976,6 +4009,14 @@ S: Maintained
F: drivers/block/pktcdvd.c
F: include/linux/pktcdvd.h
+PMC SIERRA MaxRAID DRIVER
+P: Anil Ravindranath
+M: anil_ravindranath@pmc-sierra.com
+L: linux-scsi@vger.kernel.org
+W: http://www.pmc-sierra.com/
+S: Supported
+F: drivers/scsi/pmcraid.*
+
POSIX CLOCKS and TIMERS
M: Thomas Gleixner <tglx@linutronix.de>
S: Supported
@@ -4414,7 +4455,7 @@ F: drivers/scsi/sg.c
F: include/scsi/sg.h
SCSI SUBSYSTEM
-M: "James E.J. Bottomley" <James.Bottomley@HansenPartnership.com>
+M: "James E.J. Bottomley" <James.Bottomley@suse.de>
L: linux-scsi@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-rc-fixes-2.6.git
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index aef63c8e3d2..d778a699f57 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -46,10 +46,6 @@ config GENERIC_CLOCKEVENTS_BROADCAST
depends on GENERIC_CLOCKEVENTS
default y if SMP && !LOCAL_TIMERS
-config MMU
- bool
- default y
-
config NO_IOPORT
bool
@@ -126,6 +122,13 @@ config ARCH_HAS_ILOG2_U32
config ARCH_HAS_ILOG2_U64
bool
+config ARCH_HAS_CPUFREQ
+ bool
+ help
+ Internal node to signify that the ARCH has CPUFREQ support
+ and that the relevant menu configurations are displayed for
+ it.
+
config GENERIC_HWEIGHT
bool
default y
@@ -188,6 +191,13 @@ source "kernel/Kconfig.freezer"
menu "System Type"
+config MMU
+ bool "MMU-based Paged Memory Management Support"
+ default y
+ help
+ Select if you want MMU-based virtualised addressing space
+ support by paged memory management. If unsure, say 'Y'.
+
choice
prompt "ARM system type"
default ARCH_VERSATILE
@@ -203,6 +213,7 @@ config ARCH_AAEC2000
config ARCH_INTEGRATOR
bool "ARM Ltd. Integrator family"
select ARM_AMBA
+ select ARCH_HAS_CPUFREQ
select HAVE_CLK
select COMMON_CLKDEV
select ICST525
@@ -217,6 +228,7 @@ config ARCH_REALVIEW
select ICST307
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
+ select ARCH_WANT_OPTIONAL_GPIOLIB
help
This enables support for ARM Ltd RealView boards.
@@ -229,6 +241,7 @@ config ARCH_VERSATILE
select ICST307
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
+ select ARCH_WANT_OPTIONAL_GPIOLIB
help
This enables support for ARM Ltd Versatile board.
@@ -327,6 +340,20 @@ config ARCH_H720X
help
This enables support for systems based on the Hynix HMS720x
+config ARCH_NOMADIK
+ bool "STMicroelectronics Nomadik"
+ select ARM_AMBA
+ select ARM_VIC
+ select CPU_ARM926T
+ select HAVE_CLK
+ select COMMON_CLKDEV
+ select GENERIC_TIME
+ select GENERIC_CLOCKEVENTS
+ select GENERIC_GPIO
+ select ARCH_REQUIRE_GPIOLIB
+ help
+ Support for the Nomadik platform by ST-Ericsson
+
config ARCH_IOP13XX
bool "IOP13xx-based"
depends on MMU
@@ -493,10 +520,18 @@ config ARCH_W90X900
select CPU_ARM926T
select ARCH_REQUIRE_GPIOLIB
select GENERIC_GPIO
+ select HAVE_CLK
select COMMON_CLKDEV
+ select GENERIC_TIME
+ select GENERIC_CLOCKEVENTS
help
- Support for Nuvoton (Winbond logic dept.) ARM9 processor,You
- can login www.mcuos.com or www.nuvoton.com to know more.
+ Support for Nuvoton (Winbond logic dept.) ARM9 processor,
+ At present, the w90x900 has been renamed nuc900, regarding
+ the ARM series product line, you can login the following
+ link address to know more.
+
+ <http://www.nuvoton.com/hq/enu/ProductAndSales/ProductLines/
+ ConsumerElectronicsIC/ARMMicrocontroller/ARMMicrocontroller>
config ARCH_PNX4008
bool "Philips Nexperia PNX4008 Mobile"
@@ -509,6 +544,7 @@ config ARCH_PXA
bool "PXA2xx/PXA3xx-based"
depends on MMU
select ARCH_MTD_XIP
+ select ARCH_HAS_CPUFREQ
select GENERIC_GPIO
select HAVE_CLK
select COMMON_CLKDEV
@@ -551,6 +587,7 @@ config ARCH_SA1100
select ISA
select ARCH_SPARSEMEM_ENABLE
select ARCH_MTD_XIP
+ select ARCH_HAS_CPUFREQ
select GENERIC_GPIO
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
@@ -563,6 +600,7 @@ config ARCH_SA1100
config ARCH_S3C2410
bool "Samsung S3C2410, S3C2412, S3C2413, S3C2440, S3C2442, S3C2443"
select GENERIC_GPIO
+ select ARCH_HAS_CPUFREQ
select HAVE_CLK
help
Samsung S3C2410X CPU based systems, such as the Simtec Electronics
@@ -573,9 +611,18 @@ config ARCH_S3C64XX
bool "Samsung S3C64XX"
select GENERIC_GPIO
select HAVE_CLK
+ select ARCH_HAS_CPUFREQ
help
Samsung S3C64XX series based systems
+config ARCH_S5PC1XX
+ bool "Samsung S5PC1XX"
+ select GENERIC_GPIO
+ select HAVE_CLK
+ select CPU_V7
+ help
+ Samsung S5PC1XX series based systems
+
config ARCH_SHARK
bool "Shark"
select CPU_SA110
@@ -632,11 +679,24 @@ config ARCH_OMAP
select GENERIC_GPIO
select HAVE_CLK
select ARCH_REQUIRE_GPIOLIB
+ select ARCH_HAS_CPUFREQ
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
help
Support for TI's OMAP platform (OMAP1 and OMAP2).
+config ARCH_BCMRING
+ bool "Broadcom BCMRING"
+ depends on MMU
+ select CPU_V6
+ select ARM_AMBA
+ select COMMON_CLKDEV
+ select GENERIC_TIME
+ select GENERIC_CLOCKEVENTS
+ select ARCH_WANT_OPTIONAL_GPIOLIB
+ help
+ Support for Broadcom's BCMRing platform.
+
endchoice
source "arch/arm/mach-clps711x/Kconfig"
@@ -685,6 +745,7 @@ source "arch/arm/mach-kirkwood/Kconfig"
source "arch/arm/plat-s3c24xx/Kconfig"
source "arch/arm/plat-s3c64xx/Kconfig"
source "arch/arm/plat-s3c/Kconfig"
+source "arch/arm/plat-s5pc1xx/Kconfig"
if ARCH_S3C2410
source "arch/arm/mach-s3c2400/Kconfig"
@@ -702,6 +763,10 @@ endif
source "arch/arm/plat-stmp3xxx/Kconfig"
+if ARCH_S5PC1XX
+source "arch/arm/mach-s5pc100/Kconfig"
+endif
+
source "arch/arm/mach-lh7a40x/Kconfig"
source "arch/arm/mach-h720x/Kconfig"
@@ -716,6 +781,8 @@ source "arch/arm/mach-at91/Kconfig"
source "arch/arm/plat-mxc/Kconfig"
+source "arch/arm/mach-nomadik/Kconfig"
+
source "arch/arm/mach-netx/Kconfig"
source "arch/arm/mach-ns9xxx/Kconfig"
@@ -730,6 +797,8 @@ source "arch/arm/mach-u300/Kconfig"
source "arch/arm/mach-w90x900/Kconfig"
+source "arch/arm/mach-bcmring/Kconfig"
+
# Definitions to make life easier
config ARCH_ACORN
bool
@@ -962,18 +1031,7 @@ config LOCAL_TIMERS
accounting to be spread across the timer interval, preventing a
"thundering herd" at every timer tick.
-config PREEMPT
- bool "Preemptible Kernel (EXPERIMENTAL)"
- depends on EXPERIMENTAL
- help
- This option reduces the latency of the kernel when reacting to
- real-time or interactive events by allowing a low priority process to
- be preempted even if it is in kernel mode executing a system call.
- This allows applications to run more reliably even when the system is
- under load.
-
- Say Y here if you are building a kernel for a desktop, embedded
- or real-time system. Say N if you are unsure.
+source kernel/Kconfig.preempt
config HZ
int
@@ -983,6 +1041,21 @@ config HZ
default AT91_TIMER_HZ if ARCH_AT91
default 100
+config THUMB2_KERNEL
+ bool "Compile the kernel in Thumb-2 mode"
+ depends on CPU_V7 && EXPERIMENTAL
+ select AEABI
+ select ARM_ASM_UNIFIED
+ help
+ By enabling this option, the kernel will be compiled in
+ Thumb-2 mode. A compiler/assembler that understand the unified
+ ARM-Thumb syntax is needed.
+
+ If unsure, say N.
+
+config ARM_ASM_UNIFIED
+ bool
+
config AEABI
bool "Use the ARM EABI to compile the kernel"
help
@@ -1054,6 +1127,11 @@ config HIGHMEM
If unsure, say n.
+config HIGHPTE
+ bool "Allocate 2nd-level pagetables from highmem"
+ depends on HIGHMEM
+ depends on !OUTER_CACHE
+
source "mm/Kconfig"
config LEDS
@@ -1241,7 +1319,7 @@ endmenu
menu "CPU Power Management"
-if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_PXA || ARCH_S3C64XX)
+if ARCH_HAS_CPUFREQ
source "drivers/cpufreq/Kconfig"
@@ -1276,6 +1354,52 @@ config CPU_FREQ_S3C64XX
bool "CPUfreq support for Samsung S3C64XX CPUs"
depends on CPU_FREQ && CPU_S3C6410
+config CPU_FREQ_S3C
+ bool
+ help
+ Internal configuration node for common cpufreq on Samsung SoC
+
+config CPU_FREQ_S3C24XX
+ bool "CPUfreq driver for Samsung S3C24XX series CPUs"
+ depends on ARCH_S3C2410 && CPU_FREQ && EXPERIMENTAL
+ select CPU_FREQ_S3C
+ help
+ This enables the CPUfreq driver for the Samsung S3C24XX family
+ of CPUs.
+
+ For details, take a look at <file:Documentation/cpu-freq>.
+
+ If in doubt, say N.
+
+config CPU_FREQ_S3C24XX_PLL
+ bool "Support CPUfreq changing of PLL frequency"
+ depends on CPU_FREQ_S3C24XX && EXPERIMENTAL
+ help
+ Compile in support for changing the PLL frequency from the
+ S3C24XX series CPUfreq driver. The PLL takes time to settle
+ after a frequency change, so by default it is not enabled.
+
+ This also means that the PLL tables for the selected CPU(s) will
+ be built which may increase the size of the kernel image.
+
+config CPU_FREQ_S3C24XX_DEBUG
+ bool "Debug CPUfreq Samsung driver core"
+ depends on CPU_FREQ_S3C24XX
+ help
+ Enable s3c_freq_dbg for the Samsung S3C CPUfreq core
+
+config CPU_FREQ_S3C24XX_IODEBUG
+ bool "Debug CPUfreq Samsung driver IO timing"
+ depends on CPU_FREQ_S3C24XX
+ help
+ Enable s3c_freq_iodbg for the Samsung S3C CPUfreq core
+
+config CPU_FREQ_S3C24XX_DEBUGFS
+ bool "Export debugfs for CPUFreq"
+ depends on CPU_FREQ_S3C24XX && DEBUG_FS
+ help
+ Export status information via debugfs.
+
endif
source "drivers/cpuidle/Kconfig"
@@ -1377,107 +1501,7 @@ endmenu
source "net/Kconfig"
-menu "Device Drivers"
-
-source "drivers/base/Kconfig"
-
-source "drivers/connector/Kconfig"
-
-if ALIGNMENT_TRAP || !CPU_CP15_MMU
-source "drivers/mtd/Kconfig"
-endif
-
-source "drivers/parport/Kconfig"
-
-source "drivers/pnp/Kconfig"
-
-source "drivers/block/Kconfig"
-
-# misc before ide - BLK_DEV_SGIIOC4 depends on SGI_IOC4
-
-source "drivers/misc/Kconfig"
-
-source "drivers/ide/Kconfig"
-
-source "drivers/scsi/Kconfig"
-
-source "drivers/ata/Kconfig"
-
-source "drivers/md/Kconfig"
-
-source "drivers/message/fusion/Kconfig"
-
-source "drivers/ieee1394/Kconfig"
-
-source "drivers/message/i2o/Kconfig"
-
-source "drivers/net/Kconfig"
-
-source "drivers/isdn/Kconfig"
-
-# input before char - char/joystick depends on it. As does USB.
-
-source "drivers/input/Kconfig"
-
-source "drivers/char/Kconfig"
-
-source "drivers/i2c/Kconfig"
-
-source "drivers/spi/Kconfig"
-
-source "drivers/gpio/Kconfig"
-
-source "drivers/w1/Kconfig"
-
-source "drivers/power/Kconfig"
-
-source "drivers/hwmon/Kconfig"
-
-source "drivers/thermal/Kconfig"
-
-source "drivers/watchdog/Kconfig"
-
-source "drivers/ssb/Kconfig"
-
-#source "drivers/l3/Kconfig"
-
-source "drivers/mfd/Kconfig"
-
-source "drivers/media/Kconfig"
-
-source "drivers/video/Kconfig"
-
-source "sound/Kconfig"
-
-source "drivers/hid/Kconfig"
-
-source "drivers/usb/Kconfig"
-
-source "drivers/uwb/Kconfig"
-
-source "drivers/mmc/Kconfig"
-
-source "drivers/memstick/Kconfig"
-
-source "drivers/accessibility/Kconfig"
-
-source "drivers/leds/Kconfig"
-
-source "drivers/rtc/Kconfig"
-
-source "drivers/dma/Kconfig"
-
-source "drivers/dca/Kconfig"
-
-source "drivers/auxdisplay/Kconfig"
-
-source "drivers/regulator/Kconfig"
-
-source "drivers/uio/Kconfig"
-
-source "drivers/staging/Kconfig"
-
-endmenu
+source "drivers/Kconfig"
source "fs/Kconfig"
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index a89e4734b8f..1a6f70e5292 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -8,6 +8,7 @@ source "lib/Kconfig.debug"
# n, but then RMK will have to kill you ;).
config FRAME_POINTER
bool
+ depends on !THUMB2_KERNEL
default y if !ARM_UNWIND
help
If you say N here, the resulting kernel will be slightly smaller and
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index c877d6df23d..7350557a81e 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -93,9 +93,16 @@ ifeq ($(CONFIG_ARM_UNWIND),y)
CFLAGS_ABI +=-funwind-tables
endif
+ifeq ($(CONFIG_THUMB2_KERNEL),y)
+AFLAGS_AUTOIT :=$(call as-option,-Wa$(comma)-mimplicit-it=thumb,-Wa$(comma)-mauto-it)
+AFLAGS_NOWARN :=$(call as-option,-Wa$(comma)-mno-warn-deprecated,-Wa$(comma)-W)
+CFLAGS_THUMB2 :=-mthumb $(AFLAGS_AUTOIT) $(AFLAGS_NOWARN)
+AFLAGS_THUMB2 :=$(CFLAGS_THUMB2) -Wa$(comma)-mthumb
+endif
+
# Need -Uarm for gcc < 3.x
-KBUILD_CFLAGS +=$(CFLAGS_ABI) $(arch-y) $(tune-y) $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,)) -msoft-float -Uarm
-KBUILD_AFLAGS +=$(CFLAGS_ABI) $(arch-y) $(tune-y) -msoft-float
+KBUILD_CFLAGS +=$(CFLAGS_ABI) $(CFLAGS_THUMB2) $(arch-y) $(tune-y) $(call cc-option,-mshort-load-bytes,$(call cc-option,-malignment-traps,)) -msoft-float -Uarm
+KBUILD_AFLAGS +=$(CFLAGS_ABI) $(AFLAGS_THUMB2) $(arch-y) $(tune-y) -include asm/unified.h -msoft-float
CHECKFLAGS += -D__arm__
@@ -112,6 +119,7 @@ endif
# by CONFIG_* macro name.
machine-$(CONFIG_ARCH_AAEC2000) := aaec2000
machine-$(CONFIG_ARCH_AT91) := at91
+machine-$(CONFIG_ARCH_BCMRING) := bcmring
machine-$(CONFIG_ARCH_CLPS711X) := clps711x
machine-$(CONFIG_ARCH_DAVINCI) := davinci
machine-$(CONFIG_ARCH_EBSA110) := ebsa110
@@ -135,8 +143,10 @@ machine-$(CONFIG_ARCH_MSM) := msm
machine-$(CONFIG_ARCH_MV78XX0) := mv78xx0
machine-$(CONFIG_ARCH_MX1) := mx1
machine-$(CONFIG_ARCH_MX2) := mx2
+machine-$(CONFIG_ARCH_MX25) := mx25
machine-$(CONFIG_ARCH_MX3) := mx3
machine-$(CONFIG_ARCH_NETX) := netx
+machine-$(CONFIG_ARCH_NOMADIK) := nomadik
machine-$(CONFIG_ARCH_NS9XXX) := ns9xxx
machine-$(CONFIG_ARCH_OMAP1) := omap1
machine-$(CONFIG_ARCH_OMAP2) := omap2
@@ -150,6 +160,7 @@ machine-$(CONFIG_ARCH_RPC) := rpc
machine-$(CONFIG_ARCH_S3C2410) := s3c2410 s3c2400 s3c2412 s3c2440 s3c2442 s3c2443
machine-$(CONFIG_ARCH_S3C24A0) := s3c24a0
machine-$(CONFIG_ARCH_S3C64XX) := s3c6400 s3c6410
+machine-$(CONFIG_ARCH_S5PC1XX) := s5pc100
machine-$(CONFIG_ARCH_SA1100) := sa1100
machine-$(CONFIG_ARCH_SHARK) := shark
machine-$(CONFIG_ARCH_STMP378X) := stmp378x
@@ -158,6 +169,7 @@ machine-$(CONFIG_ARCH_U300) := u300
machine-$(CONFIG_ARCH_VERSATILE) := versatile
machine-$(CONFIG_ARCH_W90X900) := w90x900
machine-$(CONFIG_FOOTBRIDGE) := footbridge
+machine-$(CONFIG_ARCH_MXC91231) := mxc91231
# Platform directory name. This list is sorted alphanumerically
# by CONFIG_* macro name.
@@ -168,6 +180,7 @@ plat-$(CONFIG_PLAT_ORION) := orion
plat-$(CONFIG_PLAT_PXA) := pxa
plat-$(CONFIG_PLAT_S3C24XX) := s3c24xx s3c
plat-$(CONFIG_PLAT_S3C64XX) := s3c64xx s3c
+plat-$(CONFIG_PLAT_S5PC1XX) := s5pc1xx s3c
plat-$(CONFIG_ARCH_STMP3XXX) := stmp3xxx
ifeq ($(CONFIG_ARCH_EBSA110),y)
diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile
index da226abce2d..4a590f4113e 100644
--- a/arch/arm/boot/Makefile
+++ b/arch/arm/boot/Makefile
@@ -61,7 +61,7 @@ endif
quiet_cmd_uimage = UIMAGE $@
cmd_uimage = $(CONFIG_SHELL) $(MKIMAGE) -A arm -O linux -T kernel \
- -C none -a $(LOADADDR) -e $(LOADADDR) \
+ -C none -a $(LOADADDR) -e $(STARTADDR) \
-n 'Linux-$(KERNELRELEASE)' -d $< $@
ifeq ($(CONFIG_ZBOOT_ROM),y)
@@ -70,6 +70,13 @@ else
$(obj)/uImage: LOADADDR=$(ZRELADDR)
endif
+ifeq ($(CONFIG_THUMB2_KERNEL),y)
+# Set bit 0 to 1 so that "mov pc, rx" switches to Thumb-2 mode
+$(obj)/uImage: STARTADDR=$(shell echo $(LOADADDR) | sed -e "s/.$$/1/")
+else
+$(obj)/uImage: STARTADDR=$(LOADADDR)
+endif
+
$(obj)/uImage: $(obj)/zImage FORCE
$(call if_changed,uimage)
@echo ' Image $@ is ready'
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 4515728c534..fa6fbf45cf3 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -140,7 +140,8 @@ start:
tst r2, #3 @ not user?
bne not_angel
mov r0, #0x17 @ angel_SWIreason_EnterSVC
- swi 0x123456 @ angel_SWI_ARM
+ ARM( swi 0x123456 ) @ angel_SWI_ARM
+ THUMB( svc 0xab ) @ angel_SWI_THUMB
not_angel:
mrs r2, cpsr @ turn off interrupts to
orr r2, r2, #0xc0 @ prevent angel from running
@@ -161,7 +162,9 @@ not_angel:
.text
adr r0, LC0
- ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp}
+ ARM( ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp} )
+ THUMB( ldmia r0, {r1, r2, r3, r4, r5, r6, ip} )
+ THUMB( ldr sp, [r0, #28] )
subs r0, r0, r1 @ calculate the delta offset
@ if delta is zero, we are
@@ -263,22 +266,25 @@ not_relocated: mov r0, #0
* r6 = processor ID
* r7 = architecture ID
* r8 = atags pointer
- * r9-r14 = corrupted
+ * r9-r12,r14 = corrupted
*/
add r1, r5, r0 @ end of decompressed kernel
adr r2, reloc_start
ldr r3, LC1
add r3, r2, r3
-1: ldmia r2!, {r9 - r14} @ copy relocation code
- stmia r1!, {r9 - r14}
- ldmia r2!, {r9 - r14}
- stmia r1!, {r9 - r14}
+1: ldmia r2!, {r9 - r12, r14} @ copy relocation code
+ stmia r1!, {r9 - r12, r14}
+ ldmia r2!, {r9 - r12, r14}
+ stmia r1!, {r9 - r12, r14}
cmp r2, r3
blo 1b
- add sp, r1, #128 @ relocate the stack
+ mov sp, r1
+ add sp, sp, #128 @ relocate the stack
bl cache_clean_flush
- add pc, r5, r0 @ call relocation code
+ ARM( add pc, r5, r0 ) @ call relocation code
+ THUMB( add r12, r5, r0 )
+ THUMB( mov pc, r12 ) @ call relocation code
/*
* We're not in danger of overwriting ourselves. Do this the simple way.
@@ -291,6 +297,7 @@ wont_overwrite: mov r0, r4
bl decompress_kernel
b call_kernel
+ .align 2
.type LC0, #object
LC0: .word LC0 @ r1
.word __bss_start @ r2
@@ -431,6 +438,7 @@ ENDPROC(__setup_mmu)
__armv4_mmu_cache_on:
mov r12, lr
+#ifdef CONFIG_MMU
bl __setup_mmu
mov r0, #0
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
@@ -444,10 +452,12 @@ __armv4_mmu_cache_on:
bl __common_mmu_cache_on
mov r0, #0
mcr p15, 0, r0, c8, c7, 0 @ flush I,D TLBs
+#endif
mov pc, r12
__armv7_mmu_cache_on:
mov r12, lr
+#ifdef CONFIG_MMU
mrc p15, 0, r11, c0, c1, 4 @ read ID_MMFR0
tst r11, #0xf @ VMSA
blne __setup_mmu
@@ -455,9 +465,11 @@ __armv7_mmu_cache_on:
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
tst r11, #0xf @ VMSA
mcrne p15, 0, r0, c8, c7, 0 @ flush I,D TLBs
+#endif
mrc p15, 0, r0, c1, c0, 0 @ read control reg
orr r0, r0, #0x5000 @ I-cache enable, RR cache replacement
orr r0, r0, #0x003c @ write buffer
+#ifdef CONFIG_MMU
#ifdef CONFIG_CPU_ENDIAN_BE8
orr r0, r0, #1 << 25 @ big-endian page tables
#endif
@@ -465,6 +477,7 @@ __armv7_mmu_cache_on:
movne r1, #-1
mcrne p15, 0, r3, c2, c0, 0 @ load page table pointer
mcrne p15, 0, r1, c3, c0, 0 @ load domain access control
+#endif
mcr p15, 0, r0, c1, c0, 0 @ load control register
mrc p15, 0, r0, c1, c0, 0 @ and read it back
mov r0, #0
@@ -498,6 +511,7 @@ __arm6_mmu_cache_on:
mov pc, r12
__common_mmu_cache_on:
+#ifndef CONFIG_THUMB2_KERNEL
#ifndef DEBUG
orr r0, r0, #0x000d @ Write buffer, mmu
#endif
@@ -509,6 +523,7 @@ __common_mmu_cache_on:
1: mcr p15, 0, r0, c1, c0, 0 @ load control register
mrc p15, 0, r0, c1, c0, 0 @ and read it back to
sub pc, lr, r0, lsr #32 @ properly flush pipeline
+#endif
/*
* All code following this line is relocatable. It is relocated by
@@ -522,7 +537,7 @@ __common_mmu_cache_on:
* r6 = processor ID
* r7 = architecture ID
* r8 = atags pointer
- * r9-r14 = corrupted
+ * r9-r12,r14 = corrupted
*/
.align 5
reloc_start: add r9, r5, r0
@@ -531,13 +546,14 @@ reloc_start: add r9, r5, r0
mov r1, r4
1:
.rept 4
- ldmia r5!, {r0, r2, r3, r10 - r14} @ relocate kernel
- stmia r1!, {r0, r2, r3, r10 - r14}
+ ldmia r5!, {r0, r2, r3, r10 - r12, r14} @ relocate kernel
+ stmia r1!, {r0, r2, r3, r10 - r12, r14}
.endr
cmp r5, r9
blo 1b
- add sp, r1, #128 @ relocate the stack
+ mov sp, r1
+ add sp, sp, #128 @ relocate the stack
debug_reloc_end
call_kernel: bl cache_clean_flush
@@ -571,7 +587,9 @@ call_cache_fn: adr r12, proc_types
ldr r2, [r12, #4] @ get mask
eor r1, r1, r6 @ (real ^ match)
tst r1, r2 @ & mask
- addeq pc, r12, r3 @ call cache function
+ ARM( addeq pc, r12, r3 ) @ call cache function
+ THUMB( addeq r12, r3 )
+ THUMB( moveq pc, r12 ) @ call cache function
add r12, r12, #4*5
b 1b
@@ -589,13 +607,15 @@ call_cache_fn: adr r12, proc_types
* methods. Writeback caches _must_ have the flush method
* defined.
*/
+ .align 2
.type proc_types,#object
proc_types:
.word 0x41560600 @ ARM6/610
.word 0xffffffe0
- b __arm6_mmu_cache_off @ works, but slow
- b __arm6_mmu_cache_off
+ W(b) __arm6_mmu_cache_off @ works, but slow
+ W(b) __arm6_mmu_cache_off
mov pc, lr
+ THUMB( nop )
@ b __arm6_mmu_cache_on @ untested
@ b __arm6_mmu_cache_off
@ b __armv3_mmu_cache_flush
@@ -603,76 +623,84 @@ proc_types:
.word 0x00000000 @ old ARM ID
.word 0x0000f000
mov pc, lr
+ THUMB( nop )
mov pc, lr
+ THUMB( nop )
mov pc, lr
+ THUMB( nop )
.word 0x41007000 @ ARM7/710
.word 0xfff8fe00
- b __arm7_mmu_cache_off
- b __arm7_mmu_cache_off
+ W(b) __arm7_mmu_cache_off
+ W(b) __arm7_mmu_cache_off
mov pc, lr
+ THUMB( nop )
.word 0x41807200 @ ARM720T (writethrough)
.word 0xffffff00
- b __armv4_mmu_cache_on
- b __armv4_mmu_cache_off
+ W(b) __armv4_mmu_cache_on
+ W(b) __armv4_mmu_cache_off
mov pc, lr
+ THUMB( nop )
.word 0x41007400 @ ARM74x
.word 0xff00ff00
- b __armv3_mpu_cache_on
- b __armv3_mpu_cache_off
- b __armv3_mpu_cache_flush
+ W(b) __armv3_mpu_cache_on
+ W(b) __armv3_mpu_cache_off
+ W(b) __armv3_mpu_cache_flush
.word 0x41009400 @ ARM94x
.word 0xff00ff00
- b __armv4_mpu_cache_on
- b __armv4_mpu_cache_off
- b __armv4_mpu_cache_flush
+ W(b) __armv4_mpu_cache_on
+ W(b) __armv4_mpu_cache_off
+ W(b) __armv4_mpu_cache_flush
.word 0x00007000 @ ARM7 IDs
.word 0x0000f000
mov pc, lr
+ THUMB( nop )
mov pc, lr
+ THUMB( nop )
mov pc, lr
+ THUMB( nop )
@ Everything from here on will be the new ID system.
.word 0x4401a100 @ sa110 / sa1100
.word 0xffffffe0
- b __armv4_mmu_cache_on
- b __armv4_mmu_cache_off
- b __armv4_mmu_cache_flush
+ W(b) __armv4_mmu_cache_on
+ W(b) __armv4_mmu_cache_off
+ W(b) __armv4_mmu_cache_flush
.word 0x6901b110 @ sa1110
.word 0xfffffff0
- b __armv4_mmu_cache_on
- b __armv4_mmu_cache_off
- b __armv4_mmu_cache_flush
+ W(b) __armv4_mmu_cache_on
+ W(b) __armv4_mmu_cache_off
+ W(b) __armv4_mmu_cache_flush
.word 0x56056930
.word 0xff0ffff0 @ PXA935
- b __armv4_mmu_cache_on
- b __armv4_mmu_cache_off
- b __armv4_mmu_cache_flush
+ W(b) __armv4_mmu_cache_on
+ W(b) __armv4_mmu_cache_off
+ W(b) __armv4_mmu_cache_flush
.word 0x56158000 @ PXA168
.word 0xfffff000
- b __armv4_mmu_cache_on
- b __armv4_mmu_cache_off
- b __armv5tej_mmu_cache_flush
+ W(b) __armv4_mmu_cache_on
+ W(b) __armv4_mmu_cache_off
+ W(b) __armv5tej_mmu_cache_flush
.word 0x56056930
.word 0xff0ffff0 @ PXA935
- b __armv4_mmu_cache_on
- b __armv4_mmu_cache_off
- b __armv4_mmu_cache_flush
+ W(b) __armv4_mmu_cache_on
+ W(b) __armv4_mmu_cache_off
+ W(b) __armv4_mmu_cache_flush
.word 0x56050000 @ Feroceon
.word 0xff0f0000
- b __armv4_mmu_cache_on
- b __armv4_mmu_cache_off
- b __armv5tej_mmu_cache_flush
+ W(b) __armv4_mmu_cache_on
+ W(b) __armv4_mmu_cache_off
+ W(b) __armv5tej_mmu_cache_flush
#ifdef CONFIG_CPU_FEROCEON_OLD_ID
/* this conflicts with the standard ARMv5TE entry */
@@ -685,47 +713,50 @@ proc_types:
.word 0x66015261 @ FA526
.word 0xff01fff1
- b __fa526_cache_on
- b __armv4_mmu_cache_off
- b __fa526_cache_flush
+ W(b) __fa526_cache_on
+ W(b) __armv4_mmu_cache_off
+ W(b) __fa526_cache_flush
@ These match on the architecture ID
.word 0x00020000 @ ARMv4T
.word 0x000f0000
- b __armv4_mmu_cache_on
- b __armv4_mmu_cache_off
- b __armv4_mmu_cache_flush
+ W(b) __armv4_mmu_cache_on
+ W(b) __armv4_mmu_cache_off
+ W(b) __armv4_mmu_cache_flush
.word 0x00050000 @ ARMv5TE
.word 0x000f0000
- b __armv4_mmu_cache_on
- b __armv4_mmu_cache_off
- b __armv4_mmu_cache_flush
+ W(b) __armv4_mmu_cache_on
+ W(b) __armv4_mmu_cache_off
+ W(b) __armv4_mmu_cache_flush
.word 0x00060000 @ ARMv5TEJ
.word 0x000f0000
- b __armv4_mmu_cache_on
- b __armv4_mmu_cache_off
- b __armv5tej_mmu_cache_flush
+ W(b) __armv4_mmu_cache_on
+ W(b) __armv4_mmu_cache_off
+ W(b) __armv4_mmu_cache_flush
.word 0x0007b000 @ ARMv6
.word 0x000ff000
- b __armv4_mmu_cache_on
- b __armv4_mmu_cache_off
- b __armv6_mmu_cache_flush
+ W(b) __armv4_mmu_cache_on
+ W(b) __armv4_mmu_cache_off
+ W(b) __armv6_mmu_cache_flush
.word 0x000f0000 @ new CPU Id
.word 0x000f0000
- b __armv7_mmu_cache_on
- b __armv7_mmu_cache_off
- b __armv7_mmu_cache_flush
+ W(b) __armv7_mmu_cache_on
+ W(b) __armv7_mmu_cache_off
+ W(b) __armv7_mmu_cache_flush
.word 0 @ unrecognised type
.word 0
mov pc, lr
+ THUMB( nop )
mov pc, lr
+ THUMB( nop )
mov pc, lr
+ THUMB( nop )
.size proc_types, . - proc_types
@@ -760,22 +791,30 @@ __armv3_mpu_cache_off:
mov pc, lr
__armv4_mmu_cache_off:
+#ifdef CONFIG_MMU
mrc p15, 0, r0, c1, c0
bic r0, r0, #0x000d
mcr p15, 0, r0, c1, c0 @ turn MMU and cache off
mov r0, #0
mcr p15, 0, r0, c7, c7 @ invalidate whole cache v4
mcr p15, 0, r0, c8, c7 @ invalidate whole TLB v4
+#endif
mov pc, lr
__armv7_mmu_cache_off:
mrc p15, 0, r0, c1, c0
+#ifdef CONFIG_MMU
bic r0, r0, #0x000d
+#else
+ bic r0, r0, #0x000c
+#endif
mcr p15, 0, r0, c1, c0 @ turn MMU and cache off
mov r12, lr
bl __armv7_mmu_cache_flush
mov r0, #0
+#ifdef CONFIG_MMU
mcr p15, 0, r0, c8, c7, 0 @ invalidate whole TLB
+#endif
mcr p15, 0, r0, c7, c5, 6 @ invalidate BTC
mcr p15, 0, r0, c7, c10, 4 @ DSB
mcr p15, 0, r0, c7, c5, 4 @ ISB
@@ -852,7 +891,7 @@ __armv7_mmu_cache_flush:
b iflush
hierarchical:
mcr p15, 0, r10, c7, c10, 5 @ DMB
- stmfd sp!, {r0-r5, r7, r9, r11}
+ stmfd sp!, {r0-r7, r9-r11}
mrc p15, 1, r0, c0, c0, 1 @ read clidr
ands r3, r0, #0x7000000 @ extract loc from clidr
mov r3, r3, lsr #23 @ left align loc bit field
@@ -877,8 +916,12 @@ loop1:
loop2:
mov r9, r4 @ create working copy of max way size
loop3:
- orr r11, r10, r9, lsl r5 @ factor way and cache number into r11
- orr r11, r11, r7, lsl r2 @ factor index number into r11
+ ARM( orr r11, r10, r9, lsl r5 ) @ factor way and cache number into r11
+ ARM( orr r11, r11, r7, lsl r2 ) @ factor index number into r11
+ THUMB( lsl r6, r9, r5 )
+ THUMB( orr r11, r10, r6 ) @ factor way and cache number into r11
+ THUMB( lsl r6, r7, r2 )
+ THUMB( orr r11, r11, r6 ) @ factor index number into r11
mcr p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way
subs r9, r9, #1 @ decrement the way
bge loop3
@@ -889,7 +932,7 @@ skip:
cmp r3, r10
bgt loop1
finished:
- ldmfd sp!, {r0-r5, r7, r9, r11}
+ ldmfd sp!, {r0-r7, r9-r11}
mov r10, #0 @ swith back to cache level 0
mcr p15, 2, r10, c0, c0, 0 @ select current cache level in cssr
iflush:
@@ -923,9 +966,13 @@ __armv4_mmu_cache_flush:
mov r11, #8
mov r11, r11, lsl r3 @ cache line size in bytes
no_cache_id:
- bic r1, pc, #63 @ align to longest cache line
+ mov r1, pc
+ bic r1, r1, #63 @ align to longest cache line
add r2, r1, r2
-1: ldr r3, [r1], r11 @ s/w flush D cache
+1:
+ ARM( ldr r3, [r1], r11 ) @ s/w flush D cache
+ THUMB( ldr r3, [r1] ) @ s/w flush D cache
+ THUMB( add r1, r1, r11 )
teq r1, r2
bne 1b
@@ -945,6 +992,7 @@ __armv3_mpu_cache_flush:
* memory, which again must be relocatable.
*/
#ifdef DEBUG
+ .align 2
.type phexbuf,#object
phexbuf: .space 12
.size phexbuf, . - phexbuf
diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c
index 6ed89836e90..920ced0b73c 100644
--- a/arch/arm/common/vic.c
+++ b/arch/arm/common/vic.c
@@ -22,10 +22,20 @@
#include <linux/list.h>
#include <linux/io.h>
#include <linux/sysdev.h>
+#include <linux/amba/bus.h>
#include <asm/mach/irq.h>
#include <asm/hardware/vic.h>
+static void vic_ack_irq(unsigned int irq)
+{
+ void __iomem *base = get_irq_chip_data(irq);
+ irq &= 31;
+ writel(1 << irq, base + VIC_INT_ENABLE_CLEAR);
+ /* moreover, clear the soft-triggered, in case it was the reason */
+ writel(1 << irq, base + VIC_INT_SOFT_CLEAR);
+}
+
static void vic_mask_irq(unsigned int irq)
{
void __iomem *base = get_irq_chip_data(irq);
@@ -253,12 +263,16 @@ static inline void vic_pm_register(void __iomem *base, unsigned int irq, u32 arg
static struct irq_chip vic_chip = {
.name = "VIC",
- .ack = vic_mask_irq,
+ .ack = vic_ack_irq,
.mask = vic_mask_irq,
.unmask = vic_unmask_irq,
.set_wake = vic_set_wake,
};
+/* The PL190 cell from ARM has been modified by ST, so handle both here */
+static void vik_init_st(void __iomem *base, unsigned int irq_start,
+ u32 vic_sources);
+
/**
* vic_init - initialise a vectored interrupt controller
* @base: iomem base address
@@ -270,6 +284,28 @@ void __init vic_init(void __iomem *base, unsigned int irq_start,
u32 vic_sources, u32 resume_sources)
{
unsigned int i;
+ u32 cellid = 0;
+ enum amba_vendor vendor;
+
+ /* Identify which VIC cell this one is, by reading the ID */
+ for (i = 0; i < 4; i++) {
+ u32 addr = ((u32)base & PAGE_MASK) + 0xfe0 + (i * 4);
+ cellid |= (readl(addr) & 0xff) << (8 * i);
+ }
+ vendor = (cellid >> 12) & 0xff;
+ printk(KERN_INFO "VIC @%p: id 0x%08x, vendor 0x%02x\n",
+ base, cellid, vendor);
+
+ switch(vendor) {
+ case AMBA_VENDOR_ST:
+ vik_init_st(base, irq_start, vic_sources);
+ return;
+ default:
+ printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n");
+ /* fall through */
+ case AMBA_VENDOR_ARM:
+ break;
+ }
/* Disable all interrupts initially. */
@@ -306,3 +342,60 @@ void __init vic_init(void __iomem *base, unsigned int irq_start,
vic_pm_register(base, irq_start, resume_sources);
}
+
+/*
+ * The PL190 cell from ARM has been modified by ST to handle 64 interrupts.
+ * The original cell has 32 interrupts, while the modified one has 64,
+ * replocating two blocks 0x00..0x1f in 0x20..0x3f. In that case
+ * the probe function is called twice, with base set to offset 000
+ * and 020 within the page. We call this "second block".
+ */
+static void __init vik_init_st(void __iomem *base, unsigned int irq_start,
+ u32 vic_sources)
+{
+ unsigned int i;
+ int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0;
+
+ /* Disable all interrupts initially. */
+
+ writel(0, base + VIC_INT_SELECT);
+ writel(0, base + VIC_INT_ENABLE);
+ writel(~0, base + VIC_INT_ENABLE_CLEAR);
+ writel(0, base + VIC_IRQ_STATUS);
+ writel(0, base + VIC_ITCR);
+ writel(~0, base + VIC_INT_SOFT_CLEAR);
+
+ /*
+ * Make sure we clear all existing interrupts. The vector registers
+ * in this cell are after the second block of general registers,
+ * so we can address them using standard offsets, but only from
+ * the second base address, which is 0x20 in the page
+ */
+ if (vic_2nd_block) {
+ writel(0, base + VIC_PL190_VECT_ADDR);
+ for (i = 0; i < 19; i++) {
+ unsigned int value;
+
+ value = readl(base + VIC_PL190_VECT_ADDR);
+ writel(value, base + VIC_PL190_VECT_ADDR);
+ }
+ /* ST has 16 vectors as well, but we don't enable them by now */
+ for (i = 0; i < 16; i++) {
+ void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4);
+ writel(0, reg);
+ }
+
+ writel(32, base + VIC_PL190_DEF_VECT_ADDR);
+ }
+
+ for (i = 0; i < 32; i++) {
+ if (vic_sources & (1 << i)) {
+ unsigned int irq = irq_start + i;
+
+ set_irq_chip(irq, &vic_chip);
+ set_irq_chip_data(irq, base);
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+ }
+}
diff --git a/arch/arm/configs/bcmring_defconfig b/arch/arm/configs/bcmring_defconfig
new file mode 100644
index 00000000000..bcc0bac551a
--- /dev/null
+++ b/arch/arm/configs/bcmring_defconfig
@@ -0,0 +1,725 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.31-rc3
+# Fri Jul 17 12:07:28 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_EXTRA_PASS=y
+# CONFIG_HOTPLUG is not set
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+# CONFIG_ELF_CORE is not set
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+# CONFIG_EPOLL is not set
+# CONFIG_SIGNALFD is not set
+# CONFIG_TIMERFD is not set
+# CONFIG_EVENTFD is not set
+CONFIG_SHMEM=y
+# CONFIG_AIO is not set
+
+#
+# Performance Counters
+#
+# CONFIG_VM_EVENT_COUNTERS is not set
+# CONFIG_SLUB_DEBUG is not set
+# CONFIG_STRIP_ASM_SYMS is not set
+# CONFIG_COMPAT_BRK is not set
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_SLOW_WORK is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+CONFIG_LBDAF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+CONFIG_DEFAULT_NOOP=y
+CONFIG_DEFAULT_IOSCHED="noop"
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_STMP3XXX is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+CONFIG_ARCH_BCMRING=y
+# CONFIG_ARCH_FPGA11107 is not set
+CONFIG_ARCH_BCM11107=y
+
+#
+# BCMRING Options
+#
+CONFIG_BCM_ZRELADDR=0x8000
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_V6=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v6=y
+CONFIG_CPU_ABRT_EV6=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_V6=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V6=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+# CONFIG_ARM_ERRATA_411920 is not set
+CONFIG_COMMON_CLKDEV=y
+
+#
+# Bus support
+#
+CONFIG_ARM_AMBA=y
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_HAVE_MLOCK=y
+CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_ALIGNMENT_TRAP=y
+CONFIG_UACCESS_WITH_MEMCPY=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0e000000
+CONFIG_ZBOOT_ROM_BSS=0x0ea00000
+CONFIG_ZBOOT_ROM=y
+CONFIG_CMDLINE=""
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+# CONFIG_PACKET is not set
+# CONFIG_UNIX is not set
+# CONFIG_NET_KEY is not set
+# CONFIG_INET is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_WIRELESS is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_GEOMETRY=y
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+# CONFIG_MTD_CFI_I2 is not set
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_OTP is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_VERIFY_WRITE=y
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+CONFIG_MTD_NAND_IDS=y
+CONFIG_MTD_NAND_BCM_UMI=y
+CONFIG_MTD_NAND_BCM_UMI_HWCS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+# CONFIG_NETDEVICES is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+# CONFIG_CONSOLE_TRANSLATIONS is not set
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+# CONFIG_DEVKMEM is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_AMBA_PL010 is not set
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=64
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+# CONFIG_GPIOLIB is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_FILE_LOCKING is not set
+# CONFIG_FSNOTIFY is not set
+# CONFIG_INOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+# CONFIG_PROC_PAGE_MONITOR is not set
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_JFFS2_FS_XATTR=y
+CONFIG_JFFS2_FS_POSIX_ACL=y
+# CONFIG_JFFS2_FS_SECURITY is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_NETWORK_FILESYSTEMS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+CONFIG_HEADERS_CHECK=y
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+# CONFIG_FTRACE is not set
+# CONFIG_BUILD_DOCSRC is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_ARM_UNWIND is not set
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_NLATTR=y
diff --git a/arch/arm/configs/cpu9260_defconfig b/arch/arm/configs/cpu9260_defconfig
new file mode 100644
index 00000000000..601e7f3d5e9
--- /dev/null
+++ b/arch/arm/configs/cpu9260_defconfig
@@ -0,0 +1,1338 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.31-rc3
+# Tue Jul 14 14:57:55 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_NET_NS is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+
+#
+# Performance Counters
+#
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_COMPAT_BRK=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_SLOW_WORK is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+CONFIG_LBDAF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+CONFIG_IOSCHED_DEADLINE=y
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_DEFAULT_AS is not set
+CONFIG_DEFAULT_DEADLINE=y
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="deadline"
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+CONFIG_ARCH_AT91=y
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_STMP3XXX is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+
+#
+# Atmel AT91 System-on-Chip
+#
+# CONFIG_ARCH_AT91RM9200 is not set
+CONFIG_ARCH_AT91SAM9260=y
+# CONFIG_ARCH_AT91SAM9261 is not set
+# CONFIG_ARCH_AT91SAM9263 is not set
+# CONFIG_ARCH_AT91SAM9RL is not set
+# CONFIG_ARCH_AT91SAM9G20 is not set
+# CONFIG_ARCH_AT91CAP9 is not set
+# CONFIG_ARCH_AT91X40 is not set
+CONFIG_AT91_PMC_UNIT=y
+
+#
+# AT91SAM9260 Variants
+#
+# CONFIG_ARCH_AT91SAM9260_SAM9XE is not set
+
+#
+# AT91SAM9260 / AT91SAM9XE Board Type
+#
+# CONFIG_MACH_AT91SAM9260EK is not set
+# CONFIG_MACH_CAM60 is not set
+# CONFIG_MACH_SAM9_L9260 is not set
+# CONFIG_MACH_AFEB9260 is not set
+# CONFIG_MACH_USB_A9260 is not set
+# CONFIG_MACH_QIL_A9260 is not set
+CONFIG_MACH_CPU9260=y
+
+#
+# AT91 Board Options
+#
+
+#
+# AT91 Feature Selections
+#
+# CONFIG_AT91_PROGRAMMABLE_CLOCKS is not set
+CONFIG_AT91_TIMER_HZ=100
+CONFIG_AT91_EARLY_DBGU=y
+# CONFIG_AT91_EARLY_USART0 is not set
+# CONFIG_AT91_EARLY_USART1 is not set
+# CONFIG_AT91_EARLY_USART2 is not set
+# CONFIG_AT91_EARLY_USART3 is not set
+# CONFIG_AT91_EARLY_USART4 is not set
+# CONFIG_AT91_EARLY_USART5 is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_ARM926T=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5TJ=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_COPY_V4WB=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+# CONFIG_ARM_THUMB is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_HAVE_MLOCK=y
+CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE=""
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_FPE_NWFPE is not set
+# CONFIG_FPE_FASTFPE is not set
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_DHCP is not set
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+CONFIG_INET_LRO=y
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_WIRELESS is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+CONFIG_MTD_RAM=y
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+CONFIG_MTD_PLATRAM=y
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=y
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_GPIO is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+CONFIG_MTD_NAND_ATMEL=y
+CONFIG_MTD_NAND_ATMEL_ECC_HW=y
+# CONFIG_MTD_NAND_ATMEL_ECC_SOFT is not set
+# CONFIG_MTD_NAND_ATMEL_ECC_NONE is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_NBD=y
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MG_DISK is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_LOWLEVEL is not set
+# CONFIG_SCSI_DH is not set
+# CONFIG_SCSI_OSD_INITIATOR is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+CONFIG_SMSC_PHY=y
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_STE10XP is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+CONFIG_MACB=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ETHOC is not set
+# CONFIG_SMC911X is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_DNET is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+# CONFIG_KS8842 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+CONFIG_PPP=y
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=y
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=y
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_LM8323 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_ATMEL=y
+CONFIG_SERIAL_ATMEL_CONSOLE=y
+CONFIG_SERIAL_ATMEL_PDC=y
+# CONFIG_SERIAL_ATMEL_TTYAT is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=32
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_ALGOBIT=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_DESIGNWARE is not set
+CONFIG_I2C_GPIO=y
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_AT91SAM9X_WATCHDOG=y
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_AB3100_CORE is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+# CONFIG_USB_DEVICEFS is not set
+# CONFIG_USB_DEVICE_CLASS is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_MON is not set
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
+# CONFIG_USB_MUSB_HDRC is not set
+# CONFIG_USB_GADGET_MUSB_HDRC is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
+#
+
+#
+# also be needed; see USB_STORAGE Help for more info
+#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+CONFIG_USB_GADGET_AT91=y
+CONFIG_USB_AT91=y
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_PXA25X is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_S3C_HSOTG is not set
+# CONFIG_USB_GADGET_IMX is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_FSL_QE is not set
+# CONFIG_USB_GADGET_CI13XXX is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LANGWELL is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+# CONFIG_USB_ZERO is not set
+# CONFIG_USB_AUDIO is not set
+CONFIG_USB_ETH=y
+CONFIG_USB_ETH_RNDIS=y
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FILE_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+
+#
+# OTG and related infrastructure
+#
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_NOP_USB_XCEIV is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
+
+#
+# MMC/SD/SDIO Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+CONFIG_MMC_AT91=y
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+# CONFIG_LEDS_PCA9532 is not set
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_GPIO_PLATFORM=y
+# CONFIG_LEDS_LP3944 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_BD2802 is not set
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+
+#
+# iptables trigger is under Netfilter config (LED target)
+#
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+CONFIG_RTC_DRV_DS1307=y
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_DRV_AT91SAM9 is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=y
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+CONFIG_CRAMFS=y
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+CONFIG_MINIX_FS=y
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+# CONFIG_FTRACE is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_ARM_UNWIND=y
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_NLATTR=y
diff --git a/arch/arm/configs/cpu9g20_defconfig b/arch/arm/configs/cpu9g20_defconfig
new file mode 100644
index 00000000000..b5b9cbbc697
--- /dev/null
+++ b/arch/arm/configs/cpu9g20_defconfig
@@ -0,0 +1,1328 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.31-rc3
+# Tue Jul 14 15:03:43 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_NET_NS is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+
+#
+# Performance Counters
+#
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_COMPAT_BRK=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_SLOW_WORK is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+CONFIG_LBDAF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+CONFIG_IOSCHED_DEADLINE=y
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_DEFAULT_AS is not set
+CONFIG_DEFAULT_DEADLINE=y
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="deadline"
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+CONFIG_ARCH_AT91=y
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_STMP3XXX is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+
+#
+# Atmel AT91 System-on-Chip
+#
+# CONFIG_ARCH_AT91RM9200 is not set
+# CONFIG_ARCH_AT91SAM9260 is not set
+# CONFIG_ARCH_AT91SAM9261 is not set
+# CONFIG_ARCH_AT91SAM9263 is not set
+# CONFIG_ARCH_AT91SAM9RL is not set
+CONFIG_ARCH_AT91SAM9G20=y
+# CONFIG_ARCH_AT91CAP9 is not set
+# CONFIG_ARCH_AT91X40 is not set
+CONFIG_AT91_PMC_UNIT=y
+
+#
+# AT91SAM9G20 Board Type
+#
+# CONFIG_MACH_AT91SAM9G20EK is not set
+CONFIG_MACH_CPU9G20=y
+
+#
+# AT91 Board Options
+#
+
+#
+# AT91 Feature Selections
+#
+# CONFIG_AT91_PROGRAMMABLE_CLOCKS is not set
+CONFIG_AT91_TIMER_HZ=100
+CONFIG_AT91_EARLY_DBGU=y
+# CONFIG_AT91_EARLY_USART0 is not set
+# CONFIG_AT91_EARLY_USART1 is not set
+# CONFIG_AT91_EARLY_USART2 is not set
+# CONFIG_AT91_EARLY_USART3 is not set
+# CONFIG_AT91_EARLY_USART4 is not set
+# CONFIG_AT91_EARLY_USART5 is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_ARM926T=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5TJ=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_COPY_V4WB=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+# CONFIG_ARM_THUMB is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_HAVE_MLOCK=y
+CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE=""
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_FPE_NWFPE is not set
+# CONFIG_FPE_FASTFPE is not set
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_DHCP is not set
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+CONFIG_INET_LRO=y
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_WIRELESS is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+CONFIG_MTD_RAM=y
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+CONFIG_MTD_PLATRAM=y
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=y
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_GPIO is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+CONFIG_MTD_NAND_ATMEL=y
+# CONFIG_MTD_NAND_ATMEL_ECC_HW is not set
+CONFIG_MTD_NAND_ATMEL_ECC_SOFT=y
+# CONFIG_MTD_NAND_ATMEL_ECC_NONE is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_NBD=y
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MG_DISK is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_LOWLEVEL is not set
+# CONFIG_SCSI_DH is not set
+# CONFIG_SCSI_OSD_INITIATOR is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+CONFIG_SMSC_PHY=y
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_STE10XP is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+CONFIG_MACB=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ETHOC is not set
+# CONFIG_SMC911X is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_DNET is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+# CONFIG_KS8842 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+CONFIG_PPP=y
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=y
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=y
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_LM8323 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_ATMEL=y
+CONFIG_SERIAL_ATMEL_CONSOLE=y
+CONFIG_SERIAL_ATMEL_PDC=y
+# CONFIG_SERIAL_ATMEL_TTYAT is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=32
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_ALGOBIT=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_DESIGNWARE is not set
+CONFIG_I2C_GPIO=y
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_AT91SAM9X_WATCHDOG=y
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_AB3100_CORE is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+# CONFIG_USB_DEVICEFS is not set
+# CONFIG_USB_DEVICE_CLASS is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_MON is not set
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
+# CONFIG_USB_MUSB_HDRC is not set
+# CONFIG_USB_GADGET_MUSB_HDRC is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
+#
+
+#
+# also be needed; see USB_STORAGE Help for more info
+#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+CONFIG_USB_GADGET_AT91=y
+CONFIG_USB_AT91=y
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_PXA25X is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_S3C_HSOTG is not set
+# CONFIG_USB_GADGET_IMX is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_FSL_QE is not set
+# CONFIG_USB_GADGET_CI13XXX is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LANGWELL is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+# CONFIG_USB_ZERO is not set
+# CONFIG_USB_AUDIO is not set
+CONFIG_USB_ETH=y
+CONFIG_USB_ETH_RNDIS=y
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FILE_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+
+#
+# OTG and related infrastructure
+#
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_NOP_USB_XCEIV is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
+
+#
+# MMC/SD/SDIO Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+CONFIG_MMC_AT91=y
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+# CONFIG_LEDS_PCA9532 is not set
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_GPIO_PLATFORM=y
+# CONFIG_LEDS_LP3944 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_BD2802 is not set
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+
+#
+# iptables trigger is under Netfilter config (LED target)
+#
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+CONFIG_RTC_DRV_DS1307=y
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_DRV_AT91SAM9 is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=y
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+CONFIG_CRAMFS=y
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+CONFIG_MINIX_FS=y
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+# CONFIG_FTRACE is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+CONFIG_ARM_UNWIND=y
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_NLATTR=y
diff --git a/arch/arm/configs/cpuat91_defconfig b/arch/arm/configs/cpuat91_defconfig
new file mode 100644
index 00000000000..4901827253f
--- /dev/null
+++ b/arch/arm/configs/cpuat91_defconfig
@@ -0,0 +1,1316 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.31-rc3
+# Tue Jul 14 14:45:01 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_NET_NS is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+
+#
+# Performance Counters
+#
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_COMPAT_BRK=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_SLOW_WORK is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+CONFIG_LBDAF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_AS is not set
+CONFIG_IOSCHED_DEADLINE=y
+# CONFIG_IOSCHED_CFQ is not set
+# CONFIG_DEFAULT_AS is not set
+CONFIG_DEFAULT_DEADLINE=y
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="deadline"
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+CONFIG_ARCH_AT91=y
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_STMP3XXX is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+
+#
+# Atmel AT91 System-on-Chip
+#
+CONFIG_ARCH_AT91RM9200=y
+# CONFIG_ARCH_AT91SAM9260 is not set
+# CONFIG_ARCH_AT91SAM9261 is not set
+# CONFIG_ARCH_AT91SAM9263 is not set
+# CONFIG_ARCH_AT91SAM9RL is not set
+# CONFIG_ARCH_AT91SAM9G20 is not set
+# CONFIG_ARCH_AT91CAP9 is not set
+# CONFIG_ARCH_AT91X40 is not set
+CONFIG_AT91_PMC_UNIT=y
+
+#
+# AT91RM9200 Board Type
+#
+# CONFIG_MACH_ONEARM is not set
+# CONFIG_ARCH_AT91RM9200DK is not set
+# CONFIG_MACH_AT91RM9200EK is not set
+# CONFIG_MACH_CSB337 is not set
+# CONFIG_MACH_CSB637 is not set
+# CONFIG_MACH_CARMEVA is not set
+# CONFIG_MACH_ATEB9200 is not set
+# CONFIG_MACH_KB9200 is not set
+# CONFIG_MACH_PICOTUX2XX is not set
+# CONFIG_MACH_KAFA is not set
+# CONFIG_MACH_ECBAT91 is not set
+# CONFIG_MACH_YL9200 is not set
+CONFIG_MACH_CPUAT91=y
+
+#
+# AT91 Board Options
+#
+
+#
+# AT91 Feature Selections
+#
+# CONFIG_AT91_PROGRAMMABLE_CLOCKS is not set
+CONFIG_AT91_TIMER_HZ=100
+CONFIG_AT91_EARLY_DBGU=y
+# CONFIG_AT91_EARLY_USART0 is not set
+# CONFIG_AT91_EARLY_USART1 is not set
+# CONFIG_AT91_EARLY_USART2 is not set
+# CONFIG_AT91_EARLY_USART3 is not set
+# CONFIG_AT91_EARLY_USART4 is not set
+# CONFIG_AT91_EARLY_USART5 is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_ARM920T=y
+CONFIG_CPU_32v4T=y
+CONFIG_CPU_ABRT_EV4T=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_V4WT=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_COPY_V4WB=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+# CONFIG_ARM_THUMB is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+# CONFIG_AEABI is not set
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_HAVE_MLOCK=y
+CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE=""
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_FPE_NWFPE is not set
+# CONFIG_FPE_FASTFPE is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_ARTHUR is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_DHCP is not set
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+CONFIG_INET_LRO=y
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+# CONFIG_WIRELESS is not set
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+CONFIG_MTD_RAM=y
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+# CONFIG_MTD_PHYSMAP_COMPAT is not set
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+CONFIG_MTD_PLATRAM=y
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_NBD=y
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MG_DISK is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_LOWLEVEL is not set
+# CONFIG_SCSI_DH is not set
+# CONFIG_SCSI_OSD_INITIATOR is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_STE10XP is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+CONFIG_ARM_AT91_ETHER=y
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ETHOC is not set
+# CONFIG_SMC911X is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_DNET is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+# CONFIG_KS8842 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+CONFIG_PPP=y
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=y
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=y
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_ATMEL=y
+CONFIG_SERIAL_ATMEL_CONSOLE=y
+CONFIG_SERIAL_ATMEL_PDC=y
+# CONFIG_SERIAL_ATMEL_TTYAT is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=32
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_ALGOBIT=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_DESIGNWARE is not set
+CONFIG_I2C_GPIO=y
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_NOWAYOUT=y
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+CONFIG_AT91RM9200_WATCHDOG=y
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_AB3100_CORE is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+# CONFIG_USB_DEVICEFS is not set
+# CONFIG_USB_DEVICE_CLASS is not set
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_MON is not set
+# CONFIG_USB_WUSB is not set
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+# CONFIG_USB_HWA_HCD is not set
+# CONFIG_USB_MUSB_HDRC is not set
+# CONFIG_USB_GADGET_MUSB_HDRC is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
+#
+
+#
+# also be needed; see USB_STORAGE Help for more info
+#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_VST is not set
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+CONFIG_USB_GADGET_AT91=y
+CONFIG_USB_AT91=y
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_PXA25X is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_S3C_HSOTG is not set
+# CONFIG_USB_GADGET_IMX is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_FSL_QE is not set
+# CONFIG_USB_GADGET_CI13XXX is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LANGWELL is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+# CONFIG_USB_ZERO is not set
+# CONFIG_USB_AUDIO is not set
+CONFIG_USB_ETH=y
+CONFIG_USB_ETH_RNDIS=y
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FILE_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+
+#
+# OTG and related infrastructure
+#
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_NOP_USB_XCEIV is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
+
+#
+# MMC/SD/SDIO Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+CONFIG_MMC_AT91=y
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+# CONFIG_LEDS_PCA9532 is not set
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_GPIO_PLATFORM=y
+# CONFIG_LEDS_LP3944 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_BD2802 is not set
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+
+#
+# iptables trigger is under Netfilter config (LED target)
+#
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+CONFIG_RTC_DRV_DS1307=y
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+CONFIG_RTC_DRV_PCF8563=y
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_DRV_AT91RM9200 is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=y
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+CONFIG_CRAMFS=y
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+CONFIG_MINIX_FS=y
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_MEMORY_INIT=y
+CONFIG_FRAME_POINTER=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+# CONFIG_FTRACE is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_NLATTR=y
diff --git a/arch/arm/configs/jornada720_defconfig b/arch/arm/configs/jornada720_defconfig
index f3074e49f2f..df9bfbea861 100644
--- a/arch/arm/configs/jornada720_defconfig
+++ b/arch/arm/configs/jornada720_defconfig
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.27-rc6
-# Tue Sep 16 18:56:58 2008
+# Linux kernel version: 2.6.31-rc6
+# Fri Aug 21 15:41:39 2009
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
@@ -9,7 +9,6 @@ CONFIG_GENERIC_GPIO=y
CONFIG_GENERIC_TIME=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_MMU=y
-# CONFIG_NO_IOPORT is not set
CONFIG_GENERIC_HARDIRQS=y
CONFIG_STACKTRACE_SUPPORT=y
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
@@ -18,16 +17,14 @@ CONFIG_TRACE_IRQFLAGS_SUPPORT=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
-# CONFIG_ARCH_HAS_ILOG2_U32 is not set
-# CONFIG_ARCH_HAS_ILOG2_U64 is not set
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
-CONFIG_ARCH_SUPPORTS_AOUT=y
CONFIG_ZONE_DMA=y
CONFIG_ARCH_MTD_XIP=y
CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
CONFIG_VECTORS_BASE=0xffff0000
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
#
# General setup
@@ -44,10 +41,19 @@ CONFIG_SYSVIPC_SYSCTL=y
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_TASKSTATS is not set
# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
# CONFIG_IKCONFIG is not set
CONFIG_LOG_BUF_SHIFT=14
-# CONFIG_CGROUPS is not set
# CONFIG_GROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
CONFIG_SYSFS_DEPRECATED=y
CONFIG_SYSFS_DEPRECATED_V2=y
# CONFIG_RELAY is not set
@@ -56,9 +62,11 @@ CONFIG_NAMESPACES=y
# CONFIG_IPC_NS is not set
# CONFIG_USER_NS is not set
# CONFIG_PID_NS is not set
+# CONFIG_NET_NS is not set
# CONFIG_BLK_DEV_INITRD is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
# CONFIG_EMBEDDED is not set
CONFIG_UID16=y
CONFIG_SYSCTL_SYSCALL=y
@@ -69,17 +77,22 @@ CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
-CONFIG_COMPAT_BRK=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
-CONFIG_ANON_INODES=y
CONFIG_EPOLL=y
CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
+CONFIG_AIO=y
+
+#
+# Performance Counters
+#
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_SLUB_DEBUG=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_COMPAT_BRK=y
# CONFIG_SLAB is not set
CONFIG_SLUB=y
# CONFIG_SLOB is not set
@@ -87,30 +100,25 @@ CONFIG_SLUB=y
# CONFIG_MARKERS is not set
CONFIG_HAVE_OPROFILE=y
# CONFIG_KPROBES is not set
-# CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS is not set
-# CONFIG_HAVE_IOREMAP_PROT is not set
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
-# CONFIG_HAVE_ARCH_TRACEHOOK is not set
-# CONFIG_HAVE_DMA_ATTRS is not set
-# CONFIG_USE_GENERIC_SMP_HELPERS is not set
CONFIG_HAVE_CLK=y
-CONFIG_PROC_PAGE_MONITOR=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_SLOW_WORK is not set
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
-# CONFIG_TINY_SHMEM is not set
CONFIG_BASE_SMALL=0
CONFIG_MODULES=y
# CONFIG_MODULE_FORCE_LOAD is not set
# CONFIG_MODULE_UNLOAD is not set
# CONFIG_MODVERSIONS is not set
# CONFIG_MODULE_SRCVERSION_ALL is not set
-CONFIG_KMOD=y
CONFIG_BLOCK=y
-# CONFIG_LBD is not set
-# CONFIG_BLK_DEV_IO_TRACE is not set
-# CONFIG_LSF is not set
+CONFIG_LBDAF=y
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_BLK_DEV_INTEGRITY is not set
@@ -126,7 +134,7 @@ CONFIG_IOSCHED_CFQ=y
CONFIG_DEFAULT_CFQ=y
# CONFIG_DEFAULT_NOOP is not set
CONFIG_DEFAULT_IOSCHED="cfq"
-CONFIG_CLASSIC_RCU=y
+CONFIG_FREEZER=y
#
# System Type
@@ -136,14 +144,15 @@ CONFIG_CLASSIC_RCU=y
# CONFIG_ARCH_REALVIEW is not set
# CONFIG_ARCH_VERSATILE is not set
# CONFIG_ARCH_AT91 is not set
-# CONFIG_ARCH_CLPS7500 is not set
# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_EP93XX is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_STMP3XXX is not set
# CONFIG_ARCH_NETX is not set
# CONFIG_ARCH_H720X is not set
-# CONFIG_ARCH_IMX is not set
# CONFIG_ARCH_IOP13XX is not set
# CONFIG_ARCH_IOP32X is not set
# CONFIG_ARCH_IOP33X is not set
@@ -152,23 +161,25 @@ CONFIG_CLASSIC_RCU=y
# CONFIG_ARCH_IXP4XX is not set
# CONFIG_ARCH_L7200 is not set
# CONFIG_ARCH_KIRKWOOD is not set
-# CONFIG_ARCH_KS8695 is not set
-# CONFIG_ARCH_NS9XXX is not set
# CONFIG_ARCH_LOKI is not set
# CONFIG_ARCH_MV78XX0 is not set
-# CONFIG_ARCH_MXC is not set
# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_W90X900 is not set
# CONFIG_ARCH_PNX4008 is not set
# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
# CONFIG_ARCH_RPC is not set
CONFIG_ARCH_SA1100=y
# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
# CONFIG_ARCH_SHARK is not set
# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_U300 is not set
# CONFIG_ARCH_DAVINCI is not set
# CONFIG_ARCH_OMAP is not set
-# CONFIG_ARCH_MSM7X00A is not set
-CONFIG_DMABOUNCE=y
#
# SA11x0 Implementations
@@ -189,14 +200,6 @@ CONFIG_SA1100_JORNADA720_SSP=y
CONFIG_SA1100_SSP=y
#
-# Boot options
-#
-
-#
-# Power management
-#
-
-#
# Processor Type
#
CONFIG_CPU_32=y
@@ -215,8 +218,8 @@ CONFIG_CPU_CP15_MMU=y
#
# CONFIG_CPU_ICACHE_DISABLE is not set
# CONFIG_CPU_DCACHE_DISABLE is not set
-# CONFIG_OUTER_CACHE is not set
CONFIG_SA1111=y
+CONFIG_DMABOUNCE=y
CONFIG_FORCE_MAX_ZONEORDER=9
#
@@ -246,30 +249,36 @@ CONFIG_TICK_ONESHOT=y
# CONFIG_NO_HZ is not set
# CONFIG_HIGH_RES_TIMERS is not set
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
# CONFIG_PREEMPT is not set
CONFIG_HZ=100
# CONFIG_AEABI is not set
-CONFIG_ARCH_DISCONTIGMEM_ENABLE=y
CONFIG_ARCH_SPARSEMEM_ENABLE=y
-CONFIG_ARCH_SELECT_MEMORY_MODEL=y
-CONFIG_NODES_SHIFT=2
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_HIGHMEM is not set
CONFIG_SELECT_MEMORY_MODEL=y
# CONFIG_FLATMEM_MANUAL is not set
-CONFIG_DISCONTIGMEM_MANUAL=y
-# CONFIG_SPARSEMEM_MANUAL is not set
-CONFIG_DISCONTIGMEM=y
-CONFIG_FLAT_NODE_MEM_MAP=y
-CONFIG_NEED_MULTIPLE_NODES=y
-# CONFIG_SPARSEMEM_STATIC is not set
-# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_HAVE_MEMORY_PRESENT=y
+CONFIG_SPARSEMEM_EXTREME=y
CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_SPLIT_PTLOCK_CPUS=4096
-# CONFIG_RESOURCES_64BIT is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
CONFIG_ZONE_DMA_FLAG=1
CONFIG_BOUNCE=y
CONFIG_VIRT_TO_BUS=y
+CONFIG_HAVE_MLOCK=y
+CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
# CONFIG_LEDS is not set
CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
#
# Boot options
@@ -281,9 +290,10 @@ CONFIG_CMDLINE=""
# CONFIG_KEXEC is not set
#
-# CPU Frequency scaling
+# CPU Power Management
#
# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
#
# Floating point emulation
@@ -294,12 +304,14 @@ CONFIG_CMDLINE=""
#
CONFIG_FPE_NWFPE=y
# CONFIG_FPE_NWFPE_XP is not set
-CONFIG_FPE_FASTFPE=y
+# CONFIG_FPE_FASTFPE is not set
#
# Userspace binary formats
#
CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
CONFIG_BINFMT_AOUT=y
# CONFIG_BINFMT_MISC is not set
# CONFIG_ARTHUR is not set
@@ -353,7 +365,6 @@ CONFIG_INET_TCP_DIAG=y
CONFIG_TCP_CONG_CUBIC=y
CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_TCP_MD5SIG is not set
-# CONFIG_IP_VS is not set
# CONFIG_IPV6 is not set
# CONFIG_NETWORK_SECMARK is not set
CONFIG_NETFILTER=y
@@ -367,10 +378,12 @@ CONFIG_NETFILTER_ADVANCED=y
# CONFIG_NETFILTER_NETLINK_LOG is not set
# CONFIG_NF_CONNTRACK is not set
# CONFIG_NETFILTER_XTABLES is not set
+# CONFIG_IP_VS is not set
#
# IP: Netfilter Configuration
#
+# CONFIG_NF_DEFRAG_IPV4 is not set
# CONFIG_IP_NF_QUEUE is not set
# CONFIG_IP_NF_IPTABLES is not set
# CONFIG_IP_NF_ARPTABLES is not set
@@ -379,6 +392,7 @@ CONFIG_NETFILTER_ADVANCED=y
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
@@ -388,7 +402,10 @@ CONFIG_NETFILTER_ADVANCED=y
# CONFIG_LAPB is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
#
# Network testing
@@ -431,14 +448,17 @@ CONFIG_IRCOMM=m
CONFIG_SA1100_FIR=m
# CONFIG_BT is not set
# CONFIG_AF_RXRPC is not set
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+# CONFIG_WIRELESS_OLD_REGULATORY is not set
+# CONFIG_WIRELESS_EXT is not set
+# CONFIG_LIB80211 is not set
#
-# Wireless
+# CFG80211 needs to be enabled for MAC80211
#
-# CONFIG_CFG80211 is not set
-# CONFIG_WIRELESS_EXT is not set
-# CONFIG_MAC80211 is not set
-# CONFIG_IEEE80211 is not set
+CONFIG_MAC80211_DEFAULT_PS_VALUE=0
+# CONFIG_WIMAX is not set
# CONFIG_RFKILL is not set
# CONFIG_NET_9P is not set
@@ -464,29 +484,34 @@ CONFIG_EXTRA_FIRMWARE=""
# CONFIG_PNP is not set
CONFIG_BLK_DEV=y
# CONFIG_BLK_DEV_COW_COMMON is not set
-CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_CRYPTOLOOP is not set
-CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_NBD=y
# CONFIG_BLK_DEV_RAM is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MG_DISK is not set
CONFIG_MISC_DEVICES=y
-# CONFIG_EEPROM_93CX6 is not set
# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+# CONFIG_EEPROM_93CX6 is not set
CONFIG_HAVE_IDE=y
CONFIG_IDE=y
-CONFIG_BLK_DEV_IDE=y
#
# Please see Documentation/ide/ide.txt for help/info on IDE drives
#
# CONFIG_BLK_DEV_IDE_SATA is not set
-CONFIG_BLK_DEV_IDEDISK=y
-# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_IDE_GD=y
+CONFIG_IDE_GD_ATA=y
+# CONFIG_IDE_GD_ATAPI is not set
CONFIG_BLK_DEV_IDECS=y
# CONFIG_BLK_DEV_IDECD is not set
# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
# CONFIG_IDE_TASK_IOCTL is not set
CONFIG_IDE_PROC_FS=y
@@ -513,8 +538,34 @@ CONFIG_DUMMY=y
# CONFIG_TUN is not set
# CONFIG_VETH is not set
# CONFIG_ARCNET is not set
-# CONFIG_NET_ETHERNET is not set
-CONFIG_MII=m
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+# CONFIG_MII is not set
+# CONFIG_AX88796 is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ETHOC is not set
+# CONFIG_SMC911X is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_DNET is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_B44 is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_KS8842 is not set
# CONFIG_NETDEV_1000 is not set
# CONFIG_NETDEV_10000 is not set
# CONFIG_TR is not set
@@ -523,17 +574,27 @@ CONFIG_MII=m
# Wireless LAN
#
# CONFIG_WLAN_PRE80211 is not set
-# CONFIG_WLAN_80211 is not set
-# CONFIG_IWLWIFI_LEDS is not set
+CONFIG_WLAN_80211=y
+# CONFIG_PCMCIA_RAYCS is not set
+# CONFIG_LIBERTAS is not set
+# CONFIG_ATMEL is not set
+# CONFIG_AIRO_CS is not set
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_HOSTAP is not set
+# CONFIG_HERMES is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
CONFIG_NET_PCMCIA=y
-CONFIG_PCMCIA_3C589=m
-CONFIG_PCMCIA_3C574=m
-CONFIG_PCMCIA_FMVJ18X=m
-CONFIG_PCMCIA_PCNET=m
-CONFIG_PCMCIA_NMCLAN=m
-CONFIG_PCMCIA_SMC91C92=m
-CONFIG_PCMCIA_XIRC2PS=m
-CONFIG_PCMCIA_AXNET=m
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+# CONFIG_PCMCIA_PCNET is not set
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_PCMCIA_AXNET is not set
# CONFIG_WAN is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
@@ -565,20 +626,23 @@ CONFIG_INPUT_MOUSEDEV_SCREEN_Y=240
#
CONFIG_INPUT_KEYBOARD=y
# CONFIG_KEYBOARD_ATKBD is not set
-# CONFIG_KEYBOARD_SUNKBD is not set
# CONFIG_KEYBOARD_LKKBD is not set
-# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+CONFIG_KEYBOARD_HP7XX=y
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_KEYBOARD_STOWAWAY is not set
-CONFIG_KEYBOARD_HP7XX=y
-# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TABLET is not set
CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_AD7879 is not set
# CONFIG_TOUCHSCREEN_FUJITSU is not set
# CONFIG_TOUCHSCREEN_GUNZE is not set
# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
# CONFIG_TOUCHSCREEN_MTOUCH is not set
# CONFIG_TOUCHSCREEN_INEXIO is not set
# CONFIG_TOUCHSCREEN_MK712 is not set
@@ -587,8 +651,8 @@ CONFIG_TOUCHSCREEN_HP7XX=y
# CONFIG_TOUCHSCREEN_PENMOUNT is not set
# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
-# CONFIG_TOUCHSCREEN_UCB1400 is not set
# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_W90X900 is not set
# CONFIG_INPUT_MISC is not set
#
@@ -624,11 +688,12 @@ CONFIG_SERIAL_SA1100_CONSOLE=y
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=32
# CONFIG_IPMI_HANDLER is not set
CONFIG_HW_RANDOM=m
-# CONFIG_NVRAM is not set
+# CONFIG_HW_RANDOM_TIMERIOMEM is not set
# CONFIG_DTLK is not set
# CONFIG_R3964 is not set
@@ -650,6 +715,10 @@ CONFIG_GPIOLIB=y
# CONFIG_GPIO_SYSFS is not set
#
+# Memory mapped GPIO expanders:
+#
+
+#
# I2C GPIO expanders:
#
@@ -663,12 +732,14 @@ CONFIG_GPIOLIB=y
# CONFIG_W1 is not set
# CONFIG_POWER_SUPPLY is not set
# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
#
# Sonics Silicon Backplane
#
-CONFIG_SSB_POSSIBLE=y
# CONFIG_SSB is not set
#
@@ -676,6 +747,7 @@ CONFIG_SSB_POSSIBLE=y
#
# CONFIG_MFD_CORE is not set
# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
# CONFIG_HTC_EGPIO is not set
# CONFIG_HTC_PASIC3 is not set
# CONFIG_MFD_TMIO is not set
@@ -687,22 +759,7 @@ CONFIG_SSB_POSSIBLE=y
# Multimedia Capabilities Port drivers
#
# CONFIG_MCP_SA11X0 is not set
-
-#
-# Multimedia devices
-#
-
-#
-# Multimedia core support
-#
-# CONFIG_VIDEO_DEV is not set
-# CONFIG_DVB_CORE is not set
-# CONFIG_VIDEO_MEDIA is not set
-
-#
-# Multimedia drivers
-#
-# CONFIG_DAB is not set
+# CONFIG_MEDIA_SUPPORT is not set
#
# Graphics support
@@ -712,6 +769,7 @@ CONFIG_SSB_POSSIBLE=y
CONFIG_FB=y
# CONFIG_FIRMWARE_EDID is not set
# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
@@ -733,7 +791,17 @@ CONFIG_FB_CFB_IMAGEBLIT=y
# CONFIG_FB_SA1100 is not set
CONFIG_FB_S1D13XXX=y
# CONFIG_FB_VIRTUAL is not set
-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_BROADSHEET is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_PLATFORM is not set
+CONFIG_LCD_HP700=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_HP700=y
#
# Display device support
@@ -757,6 +825,8 @@ CONFIG_FONT_8x16=y
# CONFIG_HID_SUPPORT is not set
# CONFIG_USB_SUPPORT is not set
# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
# CONFIG_NEW_LEDS is not set
CONFIG_RTC_LIB=y
CONFIG_RTC_CLASS=y
@@ -781,12 +851,15 @@ CONFIG_RTC_INTF_DEV=y
# Platform RTC drivers
#
# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
# CONFIG_RTC_DRV_DS1511 is not set
# CONFIG_RTC_DRV_DS1553 is not set
# CONFIG_RTC_DRV_DS1742 is not set
# CONFIG_RTC_DRV_STK17TA8 is not set
# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
# CONFIG_RTC_DRV_V3020 is not set
#
@@ -794,15 +867,10 @@ CONFIG_RTC_INTF_DEV=y
#
CONFIG_RTC_DRV_SA1100=y
# CONFIG_DMADEVICES is not set
-
-#
-# Voltage and Current regulators
-#
+# CONFIG_AUXDISPLAY is not set
# CONFIG_REGULATOR is not set
-# CONFIG_REGULATOR_FIXED_VOLTAGE is not set
-# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
-# CONFIG_REGULATOR_BQ24022 is not set
# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
#
# File systems
@@ -811,12 +879,16 @@ CONFIG_EXT2_FS=y
# CONFIG_EXT2_FS_XATTR is not set
# CONFIG_EXT2_FS_XIP is not set
# CONFIG_EXT3_FS is not set
-# CONFIG_EXT4DEV_FS is not set
+# CONFIG_EXT4_FS is not set
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
CONFIG_DNOTIFY=y
CONFIG_INOTIFY=y
CONFIG_INOTIFY_USER=y
@@ -826,6 +898,11 @@ CONFIG_INOTIFY_USER=y
# CONFIG_FUSE_FS is not set
#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
# CD-ROM/DVD Filesystems
#
# CONFIG_ISO9660_FS is not set
@@ -846,14 +923,12 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
#
CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
CONFIG_SYSFS=y
# CONFIG_TMPFS is not set
# CONFIG_HUGETLB_PAGE is not set
# CONFIG_CONFIGFS_FS is not set
-
-#
-# Miscellaneous filesystems
-#
+CONFIG_MISC_FILESYSTEMS=y
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
@@ -862,6 +937,7 @@ CONFIG_SYSFS=y
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
# CONFIG_VXFS_FS is not set
# CONFIG_MINIX_FS is not set
# CONFIG_OMFS_FS is not set
@@ -870,6 +946,7 @@ CONFIG_SYSFS=y
# CONFIG_ROMFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
+# CONFIG_NILFS2_FS is not set
# CONFIG_NETWORK_FILESYSTEMS is not set
#
@@ -935,12 +1012,16 @@ CONFIG_DEBUG_KERNEL=y
CONFIG_DETECT_SOFTLOCKUP=y
# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0
CONFIG_SCHED_DEBUG=y
# CONFIG_SCHEDSTATS is not set
# CONFIG_TIMER_STATS is not set
# CONFIG_DEBUG_OBJECTS is not set
# CONFIG_SLUB_DEBUG_ON is not set
# CONFIG_SLUB_STATS is not set
+# CONFIG_DEBUG_KMEMLEAK is not set
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_DEBUG_SPINLOCK is not set
@@ -958,19 +1039,20 @@ CONFIG_DEBUG_BUGVERBOSE=y
CONFIG_DEBUG_MEMORY_INIT=y
# CONFIG_DEBUG_LIST is not set
# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
CONFIG_FRAME_POINTER=y
# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
# CONFIG_FAULT_INJECTION is not set
# CONFIG_LATENCYTOP is not set
# CONFIG_SYSCTL_SYSCALL_CHECK is not set
-CONFIG_HAVE_FTRACE=y
-CONFIG_HAVE_DYNAMIC_FTRACE=y
+# CONFIG_PAGE_POISONING is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_TRACING_SUPPORT=y
# CONFIG_FTRACE is not set
-# CONFIG_IRQSOFF_TRACER is not set
-# CONFIG_SCHED_TRACER is not set
-# CONFIG_CONTEXT_SWITCH_TRACER is not set
# CONFIG_SAMPLES is not set
CONFIG_HAVE_ARCH_KGDB=y
# CONFIG_KGDB is not set
@@ -985,13 +1067,16 @@ CONFIG_DEBUG_LL=y
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
# CONFIG_SECURITY_FILE_CAPABILITIES is not set
CONFIG_CRYPTO=y
#
# Crypto core or helper
#
+# CONFIG_CRYPTO_FIPS is not set
# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_MANAGER2 is not set
# CONFIG_CRYPTO_GF128MUL is not set
# CONFIG_CRYPTO_NULL is not set
# CONFIG_CRYPTO_CRYPTD is not set
@@ -1062,15 +1147,21 @@ CONFIG_CRYPTO=y
# Compression
#
# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_ZLIB is not set
# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRYPTO_HW=y
+# CONFIG_BINARY_PRINTF is not set
#
# Library routines
#
CONFIG_BITREVERSE=y
-# CONFIG_GENERIC_FIND_FIRST_BIT is not set
-# CONFIG_GENERIC_FIND_NEXT_BIT is not set
+CONFIG_GENERIC_FIND_LAST_BIT=y
CONFIG_CRC_CCITT=m
# CONFIG_CRC16 is not set
# CONFIG_CRC_T10DIF is not set
@@ -1078,7 +1169,7 @@ CONFIG_CRC_CCITT=m
CONFIG_CRC32=y
# CONFIG_CRC7 is not set
# CONFIG_LIBCRC32C is not set
-CONFIG_PLIST=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_DMA=y
+CONFIG_NLATTR=y
diff --git a/arch/arm/configs/nhk8815_defconfig b/arch/arm/configs/nhk8815_defconfig
new file mode 100644
index 00000000000..9bb45b932f0
--- /dev/null
+++ b/arch/arm/configs/nhk8815_defconfig
@@ -0,0 +1,1316 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.30
+# Tue Jun 23 22:57:16 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_RD_GZIP=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+
+#
+# Performance Counters
+#
+CONFIG_VM_EVENT_COUNTERS=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_COMPAT_BRK=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_SLOW_WORK is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+CONFIG_LBDAF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+CONFIG_FREEZER=y
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_STMP3XXX is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+CONFIG_ARCH_NOMADIK=y
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+
+#
+# Nomadik boards
+#
+CONFIG_MACH_NOMADIK_8815NHK=y
+CONFIG_NOMADIK_8815=y
+CONFIG_I2C_BITBANG_8815NHK=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_ARM926T=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5TJ=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_COPY_V4WB=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
+CONFIG_OUTER_CACHE=y
+CONFIG_CACHE_L2X0=y
+CONFIG_ARM_VIC=y
+CONFIG_ARM_VIC_NR=2
+CONFIG_COMMON_CLKDEV=y
+
+#
+# Bus support
+#
+CONFIG_ARM_AMBA=y
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_HAVE_MLOCK=y
+CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE=""
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+# CONFIG_APM_EMULATION is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+CONFIG_NET_KEY=y
+# CONFIG_NET_KEY_MIGRATE is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_ASK_IP_FIB_HASH=y
+# CONFIG_IP_FIB_TRIE is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_MULTIPLE_TABLES is not set
+# CONFIG_IP_ROUTE_MULTIPATH is not set
+# CONFIG_IP_ROUTE_VERBOSE is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+CONFIG_NET_IPIP=y
+CONFIG_NET_IPGRE=y
+CONFIG_NET_IPGRE_BROADCAST=y
+CONFIG_IP_MROUTE=y
+# CONFIG_IP_PIMSM_V1 is not set
+# CONFIG_IP_PIMSM_V2 is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=y
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_NET_DSA is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_LL is not set
+CONFIG_BT_HCIVHCI=m
+# CONFIG_AF_RXRPC is not set
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_OLD_REGULATORY=y
+# CONFIG_WIRELESS_EXT is not set
+# CONFIG_LIB80211 is not set
+
+#
+# CFG80211 needs to be enabled for MAC80211
+#
+CONFIG_MAC80211_DEFAULT_PS_VALUE=0
+# CONFIG_WIMAX is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_TESTS=m
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_VERIFY_WRITE=y
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_GPIO is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+CONFIG_MTD_NAND_NOMADIK=y
+CONFIG_MTD_ONENAND=y
+CONFIG_MTD_ONENAND_VERIFY_WRITE=y
+CONFIG_MTD_ONENAND_GENERIC=y
+# CONFIG_MTD_ONENAND_OTP is not set
+# CONFIG_MTD_ONENAND_2X_PROGRAM is not set
+# CONFIG_MTD_ONENAND_SIM is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=y
+# CONFIG_BLK_DEV_NBD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MG_DISK is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_ICS932S401 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_ISL29003 is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+# CONFIG_EEPROM_AT24 is not set
+# CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
+# CONFIG_EEPROM_93CX6 is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+CONFIG_CHR_DEV_SG=y
+# CONFIG_CHR_DEV_SCH is not set
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_LIBFC is not set
+# CONFIG_LIBFCOE is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_DH is not set
+# CONFIG_SCSI_OSD_INITIATOR is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=y
+# CONFIG_VETH is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+CONFIG_SMC91X=y
+# CONFIG_DM9000 is not set
+# CONFIG_ETHOC is not set
+# CONFIG_SMC911X is not set
+# CONFIG_SMSC911X is not set
+# CONFIG_DNET is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
+# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
+# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
+# CONFIG_B44 is not set
+# CONFIG_KS8842 is not set
+CONFIG_NETDEV_1000=y
+CONFIG_NETDEV_10000=y
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_MPPE=m
+CONFIG_PPPOE=m
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+CONFIG_NETCONSOLE=m
+# CONFIG_NETCONSOLE_DYNAMIC is not set
+CONFIG_NETPOLL=y
+# CONFIG_NETPOLL_TRAP is not set
+CONFIG_NET_POLL_CONTROLLER=y
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_GPIO is not set
+CONFIG_INPUT_MOUSE=y
+# CONFIG_MOUSE_PS2 is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_BCM5974 is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_AMBA_PL010 is not set
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_ALGOBIT=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+CONFIG_I2C_GPIO=y
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+CONFIG_DEBUG_GPIO=y
+# CONFIG_GPIO_SYSFS is not set
+
+#
+# Memory mapped GPIO expanders:
+#
+# CONFIG_GPIO_PL061 is not set
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_AB3100_CORE is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_SOUND is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+# CONFIG_HID_PID is not set
+
+#
+# Special HID drivers
+#
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_WACOM is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+# CONFIG_USB is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+
+#
+# Enable Host or Gadget support to see Inventra options
+#
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# OTG and related infrastructure
+#
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_DRV_PL030 is not set
+# CONFIG_RTC_DRV_PL031 is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+CONFIG_FUSE_FS=y
+# CONFIG_CUSE is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_JFFS2_LZO is not set
+CONFIG_JFFS2_RTIME=y
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+# CONFIG_NFS_V4 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_ACL_SUPPORT=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+# CONFIG_SMB_NLS_DEFAULT is not set
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+CONFIG_CIFS_WEAK_PW_HASH=y
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+CONFIG_NLS_ISO8859_15=y
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+# CONFIG_PAGE_POISONING is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_FTRACE=y
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_PREEMPT_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_ENABLE_DEFAULT_TRACERS is not set
+# CONFIG_BOOT_TRACER is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_KMEMTRACE is not set
+# CONFIG_WORKQUEUE_TRACER is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+CONFIG_ARM_UNWIND=y
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_ERRORS is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_FIPS is not set
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_PCOMP=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+CONFIG_CRYPTO_WORKQUEUE=y
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=m
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+CONFIG_CRYPTO_SHA1=y
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+CONFIG_CRYPTO_ARC4=m
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_ZLIB is not set
+# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_HW=y
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+CONFIG_CRC_CCITT=m
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_NLATTR=y
diff --git a/arch/arm/configs/s5pc100_defconfig b/arch/arm/configs/s5pc100_defconfig
new file mode 100644
index 00000000000..b0d7d3d3a5e
--- /dev/null
+++ b/arch/arm/configs/s5pc100_defconfig
@@ -0,0 +1,892 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.30
+# Wed Jul 1 15:53:07 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_MMU=y
+CONFIG_NO_IOPORT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_CONSTRUCTORS=y
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+# CONFIG_SYSVIPC is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+
+#
+# RCU Subsystem
+#
+CONFIG_CLASSIC_RCU=y
+# CONFIG_TREE_RCU is not set
+# CONFIG_PREEMPT_RCU is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_PREEMPT_RCU_TRACE is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_GROUP_SCHED is not set
+# CONFIG_CGROUPS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+# CONFIG_UTS_NS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_RD_GZIP=y
+CONFIG_RD_BZIP2=y
+CONFIG_RD_LZMA=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+# CONFIG_EMBEDDED is not set
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+
+#
+# Performance Counters
+#
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_STRIP_ASM_SYMS is not set
+CONFIG_COMPAT_BRK=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_CLK=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_SLOW_WORK is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_BLOCK=y
+CONFIG_LBDAF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+# CONFIG_FREEZER is not set
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_STMP3XXX is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_LOKI is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_S3C64XX is not set
+CONFIG_ARCH_S5PC1XX=y
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+CONFIG_PLAT_S3C=y
+
+#
+# Boot options
+#
+# CONFIG_S3C_BOOT_ERROR_RESET is not set
+CONFIG_S3C_BOOT_UART_FORCE_FIFO=y
+
+#
+# Power management
+#
+CONFIG_S3C_LOWLEVEL_UART_PORT=0
+CONFIG_S3C_GPIO_SPACE=0
+CONFIG_S3C_GPIO_TRACK=y
+CONFIG_S3C_GPIO_PULL_UPDOWN=y
+CONFIG_PLAT_S5PC1XX=y
+CONFIG_CPU_S5PC100_INIT=y
+CONFIG_CPU_S5PC100_CLOCK=y
+CONFIG_S5PC100_SETUP_I2C0=y
+CONFIG_CPU_S5PC100=y
+CONFIG_MACH_SMDKC100=y
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_V7=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+CONFIG_CPU_PABRT_IFAR=y
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+CONFIG_HAS_TLS_REG=y
+# CONFIG_ARM_ERRATA_430973 is not set
+# CONFIG_ARM_ERRATA_458693 is not set
+# CONFIG_ARM_ERRATA_460075 is not set
+CONFIG_ARM_VIC=y
+CONFIG_ARM_VIC_NR=2
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_PREEMPT is not set
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_VIRT_TO_BUS=y
+CONFIG_HAVE_MLOCK=y
+CONFIG_HAVE_MLOCKED_PAGE_BIT=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0
+CONFIG_ZBOOT_ROM_BSS=0
+CONFIG_CMDLINE="root=/dev/mtdblock2 rootfstype=cramfs init=/linuxrc console=ttySAC2,115200 mem=128M"
+# CONFIG_XIP_KERNEL is not set
+# CONFIG_KEXEC is not set
+
+#
+# CPU Power Management
+#
+# CONFIG_CPU_IDLE is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+# CONFIG_FPE_NWFPE is not set
+# CONFIG_FPE_FASTFPE is not set
+# CONFIG_VFP is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+# CONFIG_NET is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_MTD is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=8192
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_MG_DISK is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_ICS932S401 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_ISL29003 is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+CONFIG_EEPROM_AT24=y
+# CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
+# CONFIG_EEPROM_93CX6 is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_GPIO is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_ELANTECH is not set
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_BCM5974 is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_MOUSE_SYNAPTICS_I2C is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_CONSOLE is not set
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SAMSUNG=y
+CONFIG_SERIAL_SAMSUNG_UARTS=3
+# CONFIG_SERIAL_SAMSUNG_DEBUG is not set
+CONFIG_SERIAL_SAMSUNG_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_HW_RANDOM_TIMERIOMEM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_SIMTEC is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_SPI is not set
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIOLIB=y
+# CONFIG_DEBUG_GPIO is not set
+# CONFIG_GPIO_SYSFS is not set
+
+#
+# Memory mapped GPIO expanders:
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+CONFIG_HWMON=y
+# CONFIG_HWMON_VID is not set
+# CONFIG_SENSORS_AD7414 is not set
+# CONFIG_SENSORS_AD7418 is not set
+# CONFIG_SENSORS_ADM1021 is not set
+# CONFIG_SENSORS_ADM1025 is not set
+# CONFIG_SENSORS_ADM1026 is not set
+# CONFIG_SENSORS_ADM1029 is not set
+# CONFIG_SENSORS_ADM1031 is not set
+# CONFIG_SENSORS_ADM9240 is not set
+# CONFIG_SENSORS_ADT7462 is not set
+# CONFIG_SENSORS_ADT7470 is not set
+# CONFIG_SENSORS_ADT7473 is not set
+# CONFIG_SENSORS_ADT7475 is not set
+# CONFIG_SENSORS_ATXP1 is not set
+# CONFIG_SENSORS_DS1621 is not set
+# CONFIG_SENSORS_F71805F is not set
+# CONFIG_SENSORS_F71882FG is not set
+# CONFIG_SENSORS_F75375S is not set
+# CONFIG_SENSORS_G760A is not set
+# CONFIG_SENSORS_GL518SM is not set
+# CONFIG_SENSORS_GL520SM is not set
+# CONFIG_SENSORS_IT87 is not set
+# CONFIG_SENSORS_LM63 is not set
+# CONFIG_SENSORS_LM75 is not set
+# CONFIG_SENSORS_LM77 is not set
+# CONFIG_SENSORS_LM78 is not set
+# CONFIG_SENSORS_LM80 is not set
+# CONFIG_SENSORS_LM83 is not set
+# CONFIG_SENSORS_LM85 is not set
+# CONFIG_SENSORS_LM87 is not set
+# CONFIG_SENSORS_LM90 is not set
+# CONFIG_SENSORS_LM92 is not set
+# CONFIG_SENSORS_LM93 is not set
+# CONFIG_SENSORS_LTC4215 is not set
+# CONFIG_SENSORS_LTC4245 is not set
+# CONFIG_SENSORS_LM95241 is not set
+# CONFIG_SENSORS_MAX1619 is not set
+# CONFIG_SENSORS_MAX6650 is not set
+# CONFIG_SENSORS_PC87360 is not set
+# CONFIG_SENSORS_PC87427 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_SHT15 is not set
+# CONFIG_SENSORS_DME1737 is not set
+# CONFIG_SENSORS_SMSC47M1 is not set
+# CONFIG_SENSORS_SMSC47M192 is not set
+# CONFIG_SENSORS_SMSC47B397 is not set
+# CONFIG_SENSORS_ADS7828 is not set
+# CONFIG_SENSORS_THMC50 is not set
+# CONFIG_SENSORS_TMP401 is not set
+# CONFIG_SENSORS_VT1211 is not set
+# CONFIG_SENSORS_W83781D is not set
+# CONFIG_SENSORS_W83791D is not set
+# CONFIG_SENSORS_W83792D is not set
+# CONFIG_SENSORS_W83793 is not set
+# CONFIG_SENSORS_W83L785TS is not set
+# CONFIG_SENSORS_W83L786NG is not set
+# CONFIG_SENSORS_W83627HF is not set
+# CONFIG_SENSORS_W83627EHF is not set
+# CONFIG_HWMON_DEBUG_CHIP is not set
+# CONFIG_THERMAL is not set
+# CONFIG_THERMAL_HWMON is not set
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_AB3100_CORE is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_SOUND is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+CONFIG_HID_DEBUG=y
+# CONFIG_HIDRAW is not set
+# CONFIG_HID_PID is not set
+
+#
+# Special HID drivers
+#
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+# CONFIG_USB is not set
+
+#
+# Enable Host or Gadget support to see Inventra options
+#
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# OTG and related infrastructure
+#
+CONFIG_MMC=y
+CONFIG_MMC_DEBUG=y
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD/SDIO Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+CONFIG_SDIO_UART=y
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+CONFIG_MMC_SDHCI=y
+# CONFIG_MMC_SDHCI_PLTFM is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_NEW_LEDS is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_UIO is not set
+# CONFIG_STAGING is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_XATTR=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_EXT4_FS is not set
+CONFIG_JBD=y
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+CONFIG_GENERIC_ACL=y
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=y
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+CONFIG_ROMFS_FS=y
+CONFIG_ROMFS_BACKED_BY_BLOCK=y
+# CONFIG_ROMFS_BACKED_BY_MTD is not set
+# CONFIG_ROMFS_BACKED_BY_BOTH is not set
+CONFIG_ROMFS_ON_BLOCK=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_NLS is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0
+CONFIG_SCHED_DEBUG=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
+# CONFIG_DEBUG_KMEMLEAK is not set
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_PI_LIST=y
+# CONFIG_RT_MUTEX_TESTER is not set
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+# CONFIG_PAGE_POISONING is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_FTRACE=y
+# CONFIG_FUNCTION_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_ENABLE_DEFAULT_TRACERS is not set
+# CONFIG_BOOT_TRACER is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_KMEMTRACE is not set
+# CONFIG_WORKQUEUE_TRACER is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+CONFIG_ARM_UNWIND=y
+CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_STACK_USAGE is not set
+CONFIG_DEBUG_LL=y
+# CONFIG_DEBUG_ICEDCC is not set
+CONFIG_DEBUG_S3C_PORT=y
+CONFIG_DEBUG_S3C_UART=0
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+# CONFIG_CRYPTO is not set
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_LAST_BIT=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_DECOMPRESS_BZIP2=y
+CONFIG_DECOMPRESS_LZMA=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_DMA=y
diff --git a/arch/arm/include/asm/assembler.h b/arch/arm/include/asm/assembler.h
index 15f8a092b70..00f46d9ce29 100644
--- a/arch/arm/include/asm/assembler.h
+++ b/arch/arm/include/asm/assembler.h
@@ -74,23 +74,56 @@
* Enable and disable interrupts
*/
#if __LINUX_ARM_ARCH__ >= 6
- .macro disable_irq
+ .macro disable_irq_notrace
cpsid i
.endm
- .macro enable_irq
+ .macro enable_irq_notrace
cpsie i
.endm
#else
- .macro disable_irq
+ .macro disable_irq_notrace
msr cpsr_c, #PSR_I_BIT | SVC_MODE
.endm
- .macro enable_irq
+ .macro enable_irq_notrace
msr cpsr_c, #SVC_MODE
.endm
#endif
+ .macro asm_trace_hardirqs_off
+#if defined(CONFIG_TRACE_IRQFLAGS)
+ stmdb sp!, {r0-r3, ip, lr}
+ bl trace_hardirqs_off
+ ldmia sp!, {r0-r3, ip, lr}
+#endif
+ .endm
+
+ .macro asm_trace_hardirqs_on_cond, cond
+#if defined(CONFIG_TRACE_IRQFLAGS)
+ /*
+ * actually the registers should be pushed and pop'd conditionally, but
+ * after bl the flags are certainly clobbered
+ */
+ stmdb sp!, {r0-r3, ip, lr}
+ bl\cond trace_hardirqs_on
+ ldmia sp!, {r0-r3, ip, lr}
+#endif
+ .endm
+
+ .macro asm_trace_hardirqs_on
+ asm_trace_hardirqs_on_cond al
+ .endm
+
+ .macro disable_irq
+ disable_irq_notrace
+ asm_trace_hardirqs_off
+ .endm
+
+ .macro enable_irq
+ asm_trace_hardirqs_on
+ enable_irq_notrace
+ .endm
/*
* Save the current IRQ state and disable IRQs. Note that this macro
* assumes FIQs are enabled, and that the processor is in SVC mode.
@@ -104,10 +137,16 @@
* Restore interrupt state previously stored in a register. We don't
* guarantee that this will preserve the flags.
*/
- .macro restore_irqs, oldcpsr
+ .macro restore_irqs_notrace, oldcpsr
msr cpsr_c, \oldcpsr
.endm
+ .macro restore_irqs, oldcpsr
+ tst \oldcpsr, #PSR_I_BIT
+ asm_trace_hardirqs_on_cond eq
+ restore_irqs_notrace \oldcpsr
+ .endm
+
#define USER(x...) \
9999: x; \
.section __ex_table,"a"; \
@@ -127,3 +166,87 @@
#endif
#endif
.endm
+
+#ifdef CONFIG_THUMB2_KERNEL
+ .macro setmode, mode, reg
+ mov \reg, #\mode
+ msr cpsr_c, \reg
+ .endm
+#else
+ .macro setmode, mode, reg
+ msr cpsr_c, #\mode
+ .endm
+#endif
+
+/*
+ * STRT/LDRT access macros with ARM and Thumb-2 variants
+ */
+#ifdef CONFIG_THUMB2_KERNEL
+
+ .macro usraccoff, instr, reg, ptr, inc, off, cond, abort
+9999:
+ .if \inc == 1
+ \instr\cond\()bt \reg, [\ptr, #\off]
+ .elseif \inc == 4
+ \instr\cond\()t \reg, [\ptr, #\off]
+ .else
+ .error "Unsupported inc macro argument"
+ .endif
+
+ .section __ex_table,"a"
+ .align 3
+ .long 9999b, \abort
+ .previous
+ .endm
+
+ .macro usracc, instr, reg, ptr, inc, cond, rept, abort
+ @ explicit IT instruction needed because of the label
+ @ introduced by the USER macro
+ .ifnc \cond,al
+ .if \rept == 1
+ itt \cond
+ .elseif \rept == 2
+ ittt \cond
+ .else
+ .error "Unsupported rept macro argument"
+ .endif
+ .endif
+
+ @ Slightly optimised to avoid incrementing the pointer twice
+ usraccoff \instr, \reg, \ptr, \inc, 0, \cond, \abort
+ .if \rept == 2
+ usraccoff \instr, \reg, \ptr, \inc, 4, \cond, \abort
+ .endif
+
+ add\cond \ptr, #\rept * \inc
+ .endm
+
+#else /* !CONFIG_THUMB2_KERNEL */
+
+ .macro usracc, instr, reg, ptr, inc, cond, rept, abort
+ .rept \rept
+9999:
+ .if \inc == 1
+ \instr\cond\()bt \reg, [\ptr], #\inc
+ .elseif \inc == 4
+ \instr\cond\()t \reg, [\ptr], #\inc
+ .else
+ .error "Unsupported inc macro argument"
+ .endif
+
+ .section __ex_table,"a"
+ .align 3
+ .long 9999b, \abort
+ .previous
+ .endr
+ .endm
+
+#endif /* CONFIG_THUMB2_KERNEL */
+
+ .macro strusr, reg, ptr, inc, cond=al, rept=1, abort=9001f
+ usracc str, \reg, \ptr, \inc, \cond, \rept, \abort
+ .endm
+
+ .macro ldrusr, reg, ptr, inc, cond=al, rept=1, abort=9001f
+ usracc ldr, \reg, \ptr, \inc, \cond, \rept, \abort
+ .endm
diff --git a/arch/arm/include/asm/elf.h b/arch/arm/include/asm/elf.h
index c207504de84..c3b911ee915 100644
--- a/arch/arm/include/asm/elf.h
+++ b/arch/arm/include/asm/elf.h
@@ -55,6 +55,9 @@ typedef struct user_fp elf_fpregset_t;
#define R_ARM_MOVW_ABS_NC 43
#define R_ARM_MOVT_ABS 44
+#define R_ARM_THM_CALL 10
+#define R_ARM_THM_JUMP24 30
+
/*
* These are used to set parameters in the core dumps.
*/
diff --git a/arch/arm/include/asm/ftrace.h b/arch/arm/include/asm/ftrace.h
index 39c8bc1a006..103f7ee9731 100644
--- a/arch/arm/include/asm/ftrace.h
+++ b/arch/arm/include/asm/ftrace.h
@@ -7,8 +7,43 @@
#ifndef __ASSEMBLY__
extern void mcount(void);
+extern void __gnu_mcount_nc(void);
#endif
#endif
+#ifndef __ASSEMBLY__
+
+#if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
+/*
+ * return_address uses walk_stackframe to do it's work. If both
+ * CONFIG_FRAME_POINTER=y and CONFIG_ARM_UNWIND=y walk_stackframe uses unwind
+ * information. For this to work in the function tracer many functions would
+ * have to be marked with __notrace. So for now just depend on
+ * !CONFIG_ARM_UNWIND.
+ */
+
+void *return_address(unsigned int);
+
+#else
+
+extern inline void *return_address(unsigned int level)
+{
+ return NULL;
+}
+
+#endif
+
+#define HAVE_ARCH_CALLER_ADDR
+
+#define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0))
+#define CALLER_ADDR1 ((unsigned long)return_address(1))
+#define CALLER_ADDR2 ((unsigned long)return_address(2))
+#define CALLER_ADDR3 ((unsigned long)return_address(3))
+#define CALLER_ADDR4 ((unsigned long)return_address(4))
+#define CALLER_ADDR5 ((unsigned long)return_address(5))
+#define CALLER_ADDR6 ((unsigned long)return_address(6))
+
+#endif /* ifndef __ASSEMBLY__ */
+
#endif /* _ASM_ARM_FTRACE */
diff --git a/arch/arm/include/asm/futex.h b/arch/arm/include/asm/futex.h
index 9ee743b95de..bfcc15929a7 100644
--- a/arch/arm/include/asm/futex.h
+++ b/arch/arm/include/asm/futex.h
@@ -99,6 +99,7 @@ futex_atomic_cmpxchg_inatomic(int __user *uaddr, int oldval, int newval)
__asm__ __volatile__("@futex_atomic_cmpxchg_inatomic\n"
"1: ldrt %0, [%3]\n"
" teq %0, %1\n"
+ " it eq @ explicit IT needed for the 2b label\n"
"2: streqt %2, [%3]\n"
"3:\n"
" .section __ex_table,\"a\"\n"
diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h
index 4da332b0314..b490ecc79de 100644
--- a/arch/arm/include/asm/mach/mmc.h
+++ b/arch/arm/include/asm/mach/mmc.h
@@ -10,6 +10,8 @@ struct mmc_platform_data {
unsigned int ocr_mask; /* available voltages */
u32 (*translate_vdd)(struct device *, unsigned int);
unsigned int (*status)(struct device *);
+ int gpio_wp;
+ int gpio_cd;
};
#endif
diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h
index 85763db8744..cefedf06213 100644
--- a/arch/arm/include/asm/memory.h
+++ b/arch/arm/include/asm/memory.h
@@ -44,7 +44,13 @@
* The module space lives between the addresses given by TASK_SIZE
* and PAGE_OFFSET - it must be within 32MB of the kernel text.
*/
+#ifndef CONFIG_THUMB2_KERNEL
#define MODULES_VADDR (PAGE_OFFSET - 16*1024*1024)
+#else
+/* smaller range for Thumb-2 symbols relocation (2^24)*/
+#define MODULES_VADDR (PAGE_OFFSET - 8*1024*1024)
+#endif
+
#if TASK_SIZE > MODULES_VADDR
#error Top of user space clashes with start of module space
#endif
@@ -212,7 +218,6 @@ static inline __deprecated void *bus_to_virt(unsigned long x)
*
* page_to_pfn(page) convert a struct page * to a PFN number
* pfn_to_page(pfn) convert a _valid_ PFN number to struct page *
- * pfn_valid(pfn) indicates whether a PFN number is valid
*
* virt_to_page(k) convert a _valid_ virtual address to struct page *
* virt_addr_valid(k) indicates whether a virtual address is valid
@@ -221,10 +226,6 @@ static inline __deprecated void *bus_to_virt(unsigned long x)
#define ARCH_PFN_OFFSET PHYS_PFN_OFFSET
-#ifndef CONFIG_SPARSEMEM
-#define pfn_valid(pfn) ((pfn) >= PHYS_PFN_OFFSET && (pfn) < (PHYS_PFN_OFFSET + max_mapnr))
-#endif
-
#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
#define virt_addr_valid(kaddr) ((unsigned long)(kaddr) >= PAGE_OFFSET && (unsigned long)(kaddr) < (unsigned long)high_memory)
@@ -241,18 +242,6 @@ static inline __deprecated void *bus_to_virt(unsigned long x)
#define arch_pfn_to_nid(pfn) PFN_TO_NID(pfn)
#define arch_local_page_offset(pfn, nid) LOCAL_MAP_NR((pfn) << PAGE_SHIFT)
-#define pfn_valid(pfn) \
- ({ \
- unsigned int nid = PFN_TO_NID(pfn); \
- int valid = nid < MAX_NUMNODES; \
- if (valid) { \
- pg_data_t *node = NODE_DATA(nid); \
- valid = (pfn - node->node_start_pfn) < \
- node->node_spanned_pages; \
- } \
- valid; \
- })
-
#define virt_to_page(kaddr) \
(ADDR_TO_MAPBASE(kaddr) + LOCAL_MAP_NR(kaddr))
diff --git a/arch/arm/include/asm/mmu_context.h b/arch/arm/include/asm/mmu_context.h
index 263fed05ea3..bcdb9291ef0 100644
--- a/arch/arm/include/asm/mmu_context.h
+++ b/arch/arm/include/asm/mmu_context.h
@@ -62,8 +62,10 @@ static inline void check_context(struct mm_struct *mm)
static inline void check_context(struct mm_struct *mm)
{
+#ifdef CONFIG_MMU
if (unlikely(mm->context.kvm_seq != init_mm.context.kvm_seq))
__check_kvm_seq(mm);
+#endif
}
#define init_new_context(tsk,mm) 0
diff --git a/arch/arm/include/asm/page-nommu.h b/arch/arm/include/asm/page-nommu.h
index 3574c0deb37..d1b162a18dc 100644
--- a/arch/arm/include/asm/page-nommu.h
+++ b/arch/arm/include/asm/page-nommu.h
@@ -43,7 +43,4 @@ typedef unsigned long pgprot_t;
#define __pmd(x) (x)
#define __pgprot(x) (x)
-extern unsigned long memory_start;
-extern unsigned long memory_end;
-
#endif
diff --git a/arch/arm/include/asm/page.h b/arch/arm/include/asm/page.h
index 9c746af1bf6..3a32af4cce3 100644
--- a/arch/arm/include/asm/page.h
+++ b/arch/arm/include/asm/page.h
@@ -194,6 +194,10 @@ typedef unsigned long pgprot_t;
typedef struct page *pgtable_t;
+#ifndef CONFIG_SPARSEMEM
+extern int pfn_valid(unsigned long);
+#endif
+
#include <asm/memory.h>
#endif /* !__ASSEMBLY__ */
diff --git a/arch/arm/include/asm/pgalloc.h b/arch/arm/include/asm/pgalloc.h
index 3dcd64bf182..b12cc98bbe0 100644
--- a/arch/arm/include/asm/pgalloc.h
+++ b/arch/arm/include/asm/pgalloc.h
@@ -36,6 +36,8 @@ extern void free_pgd_slow(struct mm_struct *mm, pgd_t *pgd);
#define pgd_alloc(mm) get_pgd_slow(mm)
#define pgd_free(mm, pgd) free_pgd_slow(mm, pgd)
+#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO)
+
/*
* Allocate one PTE table.
*
@@ -57,7 +59,7 @@ pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)
{
pte_t *pte;
- pte = (pte_t *)__get_free_page(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO);
+ pte = (pte_t *)__get_free_page(PGALLOC_GFP);
if (pte) {
clean_dcache_area(pte, sizeof(pte_t) * PTRS_PER_PTE);
pte += PTRS_PER_PTE;
@@ -71,10 +73,16 @@ pte_alloc_one(struct mm_struct *mm, unsigned long addr)
{
struct page *pte;
- pte = alloc_pages(GFP_KERNEL|__GFP_REPEAT|__GFP_ZERO, 0);
+#ifdef CONFIG_HIGHPTE
+ pte = alloc_pages(PGALLOC_GFP | __GFP_HIGHMEM, 0);
+#else
+ pte = alloc_pages(PGALLOC_GFP, 0);
+#endif
if (pte) {
- void *page = page_address(pte);
- clean_dcache_area(page, sizeof(pte_t) * PTRS_PER_PTE);
+ if (!PageHighMem(pte)) {
+ void *page = page_address(pte);
+ clean_dcache_area(page, sizeof(pte_t) * PTRS_PER_PTE);
+ }
pgtable_page_ctor(pte);
}
diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index c433c6c7311..201ccaa11f6 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -162,10 +162,8 @@ extern void __pgd_error(const char *file, int line, unsigned long val);
* entries are stored 1024 bytes below.
*/
#define L_PTE_PRESENT (1 << 0)
-#define L_PTE_FILE (1 << 1) /* only when !PRESENT */
#define L_PTE_YOUNG (1 << 1)
-#define L_PTE_BUFFERABLE (1 << 2) /* obsolete, matches PTE */
-#define L_PTE_CACHEABLE (1 << 3) /* obsolete, matches PTE */
+#define L_PTE_FILE (1 << 2) /* only when !PRESENT */
#define L_PTE_DIRTY (1 << 6)
#define L_PTE_WRITE (1 << 7)
#define L_PTE_USER (1 << 8)
@@ -264,10 +262,19 @@ extern struct page *empty_zero_page;
#define pte_clear(mm,addr,ptep) set_pte_ext(ptep, __pte(0), 0)
#define pte_page(pte) (pfn_to_page(pte_pfn(pte)))
#define pte_offset_kernel(dir,addr) (pmd_page_vaddr(*(dir)) + __pte_index(addr))
-#define pte_offset_map(dir,addr) (pmd_page_vaddr(*(dir)) + __pte_index(addr))
-#define pte_offset_map_nested(dir,addr) (pmd_page_vaddr(*(dir)) + __pte_index(addr))
-#define pte_unmap(pte) do { } while (0)
-#define pte_unmap_nested(pte) do { } while (0)
+
+#define pte_offset_map(dir,addr) (__pte_map(dir, KM_PTE0) + __pte_index(addr))
+#define pte_offset_map_nested(dir,addr) (__pte_map(dir, KM_PTE1) + __pte_index(addr))
+#define pte_unmap(pte) __pte_unmap(pte, KM_PTE0)
+#define pte_unmap_nested(pte) __pte_unmap(pte, KM_PTE1)
+
+#ifndef CONFIG_HIGHPTE
+#define __pte_map(dir,km) pmd_page_vaddr(*(dir))
+#define __pte_unmap(pte,km) do { } while (0)
+#else
+#define __pte_map(dir,km) ((pte_t *)kmap_atomic(pmd_page(*(dir)), km) + PTRS_PER_PTE)
+#define __pte_unmap(pte,km) kunmap_atomic((pte - PTRS_PER_PTE), km)
+#endif
#define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)
@@ -381,13 +388,13 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
*
* 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
- * <--------------- offset --------------------> <--- type --> 0 0
+ * <--------------- offset --------------------> <- type --> 0 0 0
*
- * This gives us up to 127 swap files and 32GB per swap file. Note that
+ * This gives us up to 63 swap files and 32GB per swap file. Note that
* the offset field is always non-zero.
*/
-#define __SWP_TYPE_SHIFT 2
-#define __SWP_TYPE_BITS 7
+#define __SWP_TYPE_SHIFT 3
+#define __SWP_TYPE_BITS 6
#define __SWP_TYPE_MASK ((1 << __SWP_TYPE_BITS) - 1)
#define __SWP_OFFSET_SHIFT (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT)
@@ -411,13 +418,13 @@ extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
*
* 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
- * <------------------------ offset -------------------------> 1 0
+ * <----------------------- offset ------------------------> 1 0 0
*/
#define pte_file(pte) (pte_val(pte) & L_PTE_FILE)
-#define pte_to_pgoff(x) (pte_val(x) >> 2)
-#define pgoff_to_pte(x) __pte(((x) << 2) | L_PTE_FILE)
+#define pte_to_pgoff(x) (pte_val(x) >> 3)
+#define pgoff_to_pte(x) __pte(((x) << 3) | L_PTE_FILE)
-#define PTE_FILE_MAX_BITS 30
+#define PTE_FILE_MAX_BITS 29
/* Needs to be defined here and not in linux/mm.h, as it is arch dependent */
/* FIXME: this is not correct */
diff --git a/arch/arm/include/asm/ptrace.h b/arch/arm/include/asm/ptrace.h
index 67b833c9b6b..bbecccda76d 100644
--- a/arch/arm/include/asm/ptrace.h
+++ b/arch/arm/include/asm/ptrace.h
@@ -82,6 +82,14 @@
#define PSR_ENDSTATE 0
#endif
+/*
+ * These are 'magic' values for PTRACE_PEEKUSR that return info about where a
+ * process is located in memory.
+ */
+#define PT_TEXT_ADDR 0x10000
+#define PT_DATA_ADDR 0x10004
+#define PT_TEXT_END_ADDR 0x10008
+
#ifndef __ASSEMBLY__
/*
diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h
index d3a39b1e6c0..2dfb7d7a66e 100644
--- a/arch/arm/include/asm/thread_info.h
+++ b/arch/arm/include/asm/thread_info.h
@@ -142,6 +142,7 @@ extern void vfp_sync_state(struct thread_info *thread);
#define TIF_USING_IWMMXT 17
#define TIF_MEMDIE 18
#define TIF_FREEZE 19
+#define TIF_RESTORE_SIGMASK 20
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
@@ -150,6 +151,7 @@ extern void vfp_sync_state(struct thread_info *thread);
#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG)
#define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT)
#define _TIF_FREEZE (1 << TIF_FREEZE)
+#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK)
/*
* Change these and you break ASM code in entry-common.S
diff --git a/arch/arm/include/asm/uaccess.h b/arch/arm/include/asm/uaccess.h
index 0da9bc9b3b1..1d6bd40a432 100644
--- a/arch/arm/include/asm/uaccess.h
+++ b/arch/arm/include/asm/uaccess.h
@@ -17,6 +17,7 @@
#include <asm/memory.h>
#include <asm/domain.h>
#include <asm/system.h>
+#include <asm/unified.h>
#define VERIFY_READ 0
#define VERIFY_WRITE 1
@@ -365,8 +366,10 @@ do { \
#define __put_user_asm_dword(x,__pu_addr,err) \
__asm__ __volatile__( \
- "1: strt " __reg_oper1 ", [%1], #4\n" \
- "2: strt " __reg_oper0 ", [%1]\n" \
+ ARM( "1: strt " __reg_oper1 ", [%1], #4\n" ) \
+ ARM( "2: strt " __reg_oper0 ", [%1]\n" ) \
+ THUMB( "1: strt " __reg_oper1 ", [%1]\n" ) \
+ THUMB( "2: strt " __reg_oper0 ", [%1, #4]\n" ) \
"3:\n" \
" .section .fixup,\"ax\"\n" \
" .align 2\n" \
diff --git a/arch/arm/include/asm/unified.h b/arch/arm/include/asm/unified.h
new file mode 100644
index 00000000000..073e85b9b96
--- /dev/null
+++ b/arch/arm/include/asm/unified.h
@@ -0,0 +1,126 @@
+/*
+ * include/asm-arm/unified.h - Unified Assembler Syntax helper macros
+ *
+ * Copyright (C) 2008 ARM Limited
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ASM_UNIFIED_H
+#define __ASM_UNIFIED_H
+
+#if defined(__ASSEMBLY__) && defined(CONFIG_ARM_ASM_UNIFIED)
+ .syntax unified
+#endif
+
+#ifdef CONFIG_THUMB2_KERNEL
+
+#if __GNUC__ < 4
+#error Thumb-2 kernel requires gcc >= 4
+#endif
+
+/* The CPSR bit describing the instruction set (Thumb) */
+#define PSR_ISETSTATE PSR_T_BIT
+
+#define ARM(x...)
+#define THUMB(x...) x
+#define W(instr) instr.w
+#define BSYM(sym) sym + 1
+
+#else /* !CONFIG_THUMB2_KERNEL */
+
+/* The CPSR bit describing the instruction set (ARM) */
+#define PSR_ISETSTATE 0
+
+#define ARM(x...) x
+#define THUMB(x...)
+#define W(instr) instr
+#define BSYM(sym) sym
+
+#endif /* CONFIG_THUMB2_KERNEL */
+
+#ifndef CONFIG_ARM_ASM_UNIFIED
+
+/*
+ * If the unified assembly syntax isn't used (in ARM mode), these
+ * macros expand to an empty string
+ */
+#ifdef __ASSEMBLY__
+ .macro it, cond
+ .endm
+ .macro itt, cond
+ .endm
+ .macro ite, cond
+ .endm
+ .macro ittt, cond
+ .endm
+ .macro itte, cond
+ .endm
+ .macro itet, cond
+ .endm
+ .macro itee, cond
+ .endm
+ .macro itttt, cond
+ .endm
+ .macro ittte, cond
+ .endm
+ .macro ittet, cond
+ .endm
+ .macro ittee, cond
+ .endm
+ .macro itett, cond
+ .endm
+ .macro itete, cond
+ .endm
+ .macro iteet, cond
+ .endm
+ .macro iteee, cond
+ .endm
+#else /* !__ASSEMBLY__ */
+__asm__(
+" .macro it, cond\n"
+" .endm\n"
+" .macro itt, cond\n"
+" .endm\n"
+" .macro ite, cond\n"
+" .endm\n"
+" .macro ittt, cond\n"
+" .endm\n"
+" .macro itte, cond\n"
+" .endm\n"
+" .macro itet, cond\n"
+" .endm\n"
+" .macro itee, cond\n"
+" .endm\n"
+" .macro itttt, cond\n"
+" .endm\n"
+" .macro ittte, cond\n"
+" .endm\n"
+" .macro ittet, cond\n"
+" .endm\n"
+" .macro ittee, cond\n"
+" .endm\n"
+" .macro itett, cond\n"
+" .endm\n"
+" .macro itete, cond\n"
+" .endm\n"
+" .macro iteet, cond\n"
+" .endm\n"
+" .macro iteee, cond\n"
+" .endm\n");
+#endif /* __ASSEMBLY__ */
+
+#endif /* CONFIG_ARM_ASM_UNIFIED */
+
+#endif /* !__ASM_UNIFIED_H */
diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h
index 0e97b8cb77d..9122c9ee18f 100644
--- a/arch/arm/include/asm/unistd.h
+++ b/arch/arm/include/asm/unistd.h
@@ -360,8 +360,8 @@
#define __NR_readlinkat (__NR_SYSCALL_BASE+332)
#define __NR_fchmodat (__NR_SYSCALL_BASE+333)
#define __NR_faccessat (__NR_SYSCALL_BASE+334)
- /* 335 for pselect6 */
- /* 336 for ppoll */
+#define __NR_pselect6 (__NR_SYSCALL_BASE+335)
+#define __NR_ppoll (__NR_SYSCALL_BASE+336)
#define __NR_unshare (__NR_SYSCALL_BASE+337)
#define __NR_set_robust_list (__NR_SYSCALL_BASE+338)
#define __NR_get_robust_list (__NR_SYSCALL_BASE+339)
@@ -372,7 +372,7 @@
#define __NR_vmsplice (__NR_SYSCALL_BASE+343)
#define __NR_move_pages (__NR_SYSCALL_BASE+344)
#define __NR_getcpu (__NR_SYSCALL_BASE+345)
- /* 346 for epoll_pwait */
+#define __NR_epoll_pwait (__NR_SYSCALL_BASE+346)
#define __NR_kexec_load (__NR_SYSCALL_BASE+347)
#define __NR_utimensat (__NR_SYSCALL_BASE+348)
#define __NR_signalfd (__NR_SYSCALL_BASE+349)
@@ -432,6 +432,7 @@
#define __ARCH_WANT_SYS_SIGPENDING
#define __ARCH_WANT_SYS_SIGPROCMASK
#define __ARCH_WANT_SYS_RT_SIGACTION
+#define __ARCH_WANT_SYS_RT_SIGSUSPEND
#if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT)
#define __ARCH_WANT_SYS_TIME
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index ff89d0b3abc..3213c9382b1 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -8,10 +8,12 @@ ifdef CONFIG_DYNAMIC_FTRACE
CFLAGS_REMOVE_ftrace.o = -pg
endif
+CFLAGS_REMOVE_return_address.o = -pg
+
# Object file lists.
obj-y := compat.o elf.o entry-armv.o entry-common.o irq.o \
- process.o ptrace.o setup.o signal.o \
+ process.o ptrace.o return_address.o setup.o signal.o \
sys_arm.o stacktrace.o time.o traps.o
obj-$(CONFIG_ISA_DMA_API) += dma.o
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index 531e1860e54..0e627705f74 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -186,4 +186,5 @@ EXPORT_SYMBOL(_find_next_bit_be);
#ifdef CONFIG_FUNCTION_TRACER
EXPORT_SYMBOL(mcount);
+EXPORT_SYMBOL(__gnu_mcount_nc);
#endif
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
index f776e72a4cb..ecfa98954d1 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -81,7 +81,7 @@
CALL(sys_ni_syscall) /* was sys_ssetmask */
/* 70 */ CALL(sys_setreuid16)
CALL(sys_setregid16)
- CALL(sys_sigsuspend_wrapper)
+ CALL(sys_sigsuspend)
CALL(sys_sigpending)
CALL(sys_sethostname)
/* 75 */ CALL(sys_setrlimit)
@@ -188,7 +188,7 @@
CALL(sys_rt_sigpending)
CALL(sys_rt_sigtimedwait)
CALL(sys_rt_sigqueueinfo)
- CALL(sys_rt_sigsuspend_wrapper)
+ CALL(sys_rt_sigsuspend)
/* 180 */ CALL(ABI(sys_pread64, sys_oabi_pread64))
CALL(ABI(sys_pwrite64, sys_oabi_pwrite64))
CALL(sys_chown16)
@@ -344,8 +344,8 @@
CALL(sys_readlinkat)
CALL(sys_fchmodat)
CALL(sys_faccessat)
-/* 335 */ CALL(sys_ni_syscall) /* eventually pselect6 */
- CALL(sys_ni_syscall) /* eventually ppoll */
+/* 335 */ CALL(sys_pselect6)
+ CALL(sys_ppoll)
CALL(sys_unshare)
CALL(sys_set_robust_list)
CALL(sys_get_robust_list)
@@ -355,7 +355,7 @@
CALL(sys_vmsplice)
CALL(sys_move_pages)
/* 345 */ CALL(sys_getcpu)
- CALL(sys_ni_syscall) /* eventually epoll_pwait */
+ CALL(sys_epoll_pwait)
CALL(sys_kexec_load)
CALL(sys_utimensat)
CALL(sys_signalfd)
diff --git a/arch/arm/kernel/crunch.c b/arch/arm/kernel/crunch.c
index 99995c2b231..769abe15cf9 100644
--- a/arch/arm/kernel/crunch.c
+++ b/arch/arm/kernel/crunch.c
@@ -31,7 +31,7 @@ void crunch_task_release(struct thread_info *thread)
static int crunch_enabled(u32 devcfg)
{
- return !!(devcfg & EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE);
+ return !!(devcfg & EP93XX_SYSCON_DEVCFG_CPENA);
}
static int crunch_do(struct notifier_block *self, unsigned long cmd, void *t)
@@ -56,11 +56,16 @@ static int crunch_do(struct notifier_block *self, unsigned long cmd, void *t)
break;
case THREAD_NOTIFY_SWITCH:
- devcfg = __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG);
+ devcfg = __raw_readl(EP93XX_SYSCON_DEVCFG);
if (crunch_enabled(devcfg) || crunch_owner == crunch_state) {
- devcfg ^= EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE;
+ /*
+ * We don't use ep93xx_syscon_swlocked_write() here
+ * because we are on the context switch path and
+ * preemption is already disabled.
+ */
+ devcfg ^= EP93XX_SYSCON_DEVCFG_CPENA;
__raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
- __raw_writel(devcfg, EP93XX_SYSCON_DEVICE_CONFIG);
+ __raw_writel(devcfg, EP93XX_SYSCON_DEVCFG);
}
break;
}
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index fc8af43c500..3d727a8a23b 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -34,7 +34,7 @@
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
- adrne lr, 1b
+ adrne lr, BSYM(1b)
bne asm_do_IRQ
#ifdef CONFIG_SMP
@@ -46,13 +46,13 @@
*/
test_for_ipi r0, r6, r5, lr
movne r0, sp
- adrne lr, 1b
+ adrne lr, BSYM(1b)
bne do_IPI
#ifdef CONFIG_LOCAL_TIMERS
test_for_ltirq r0, r6, r5, lr
movne r0, sp
- adrne lr, 1b
+ adrne lr, BSYM(1b)
bne do_local_timer
#endif
#endif
@@ -70,7 +70,10 @@
*/
.macro inv_entry, reason
sub sp, sp, #S_FRAME_SIZE
- stmib sp, {r1 - lr}
+ ARM( stmib sp, {r1 - lr} )
+ THUMB( stmia sp, {r0 - r12} )
+ THUMB( str sp, [sp, #S_SP] )
+ THUMB( str lr, [sp, #S_LR] )
mov r1, #\reason
.endm
@@ -126,17 +129,24 @@ ENDPROC(__und_invalid)
.macro svc_entry, stack_hole=0
UNWIND(.fnstart )
UNWIND(.save {r0 - pc} )
- sub sp, sp, #(S_FRAME_SIZE + \stack_hole)
+ sub sp, sp, #(S_FRAME_SIZE + \stack_hole - 4)
+#ifdef CONFIG_THUMB2_KERNEL
+ SPFIX( str r0, [sp] ) @ temporarily saved
+ SPFIX( mov r0, sp )
+ SPFIX( tst r0, #4 ) @ test original stack alignment
+ SPFIX( ldr r0, [sp] ) @ restored
+#else
SPFIX( tst sp, #4 )
- SPFIX( bicne sp, sp, #4 )
- stmib sp, {r1 - r12}
+#endif
+ SPFIX( subeq sp, sp, #4 )
+ stmia sp, {r1 - r12}
ldmia r0, {r1 - r3}
- add r5, sp, #S_SP @ here for interlock avoidance
+ add r5, sp, #S_SP - 4 @ here for interlock avoidance
mov r4, #-1 @ "" "" "" ""
- add r0, sp, #(S_FRAME_SIZE + \stack_hole)
- SPFIX( addne r0, r0, #4 )
- str r1, [sp] @ save the "real" r0 copied
+ add r0, sp, #(S_FRAME_SIZE + \stack_hole - 4)
+ SPFIX( addeq r0, r0, #4 )
+ str r1, [sp, #-4]! @ save the "real" r0 copied
@ from the exception stack
mov r1, lr
@@ -151,6 +161,8 @@ ENDPROC(__und_invalid)
@ r4 - orig_r0 (see pt_regs definition in ptrace.h)
@
stmia r5, {r0 - r4}
+
+ asm_trace_hardirqs_off
.endm
.align 5
@@ -196,9 +208,8 @@ __dabt_svc:
@
@ restore SPSR and restart the instruction
@
- ldr r0, [sp, #S_PSR]
- msr spsr_cxsf, r0
- ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
+ ldr r2, [sp, #S_PSR]
+ svc_exit r2 @ return from exception
UNWIND(.fnend )
ENDPROC(__dabt_svc)
@@ -206,9 +217,6 @@ ENDPROC(__dabt_svc)
__irq_svc:
svc_entry
-#ifdef CONFIG_TRACE_IRQFLAGS
- bl trace_hardirqs_off
-#endif
#ifdef CONFIG_PREEMPT
get_thread_info tsk
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
@@ -225,13 +233,12 @@ __irq_svc:
tst r0, #_TIF_NEED_RESCHED
blne svc_preempt
#endif
- ldr r0, [sp, #S_PSR] @ irqs are already disabled
- msr spsr_cxsf, r0
+ ldr r4, [sp, #S_PSR] @ irqs are already disabled
#ifdef CONFIG_TRACE_IRQFLAGS
- tst r0, #PSR_I_BIT
+ tst r4, #PSR_I_BIT
bleq trace_hardirqs_on
#endif
- ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
+ svc_exit r4 @ return from exception
UNWIND(.fnend )
ENDPROC(__irq_svc)
@@ -266,7 +273,7 @@ __und_svc:
@ r0 - instruction
@
ldr r0, [r2, #-4]
- adr r9, 1f
+ adr r9, BSYM(1f)
bl call_fpe
mov r0, sp @ struct pt_regs *regs
@@ -280,9 +287,8 @@ __und_svc:
@
@ restore SPSR and restart the instruction
@
- ldr lr, [sp, #S_PSR] @ Get SVC cpsr
- msr spsr_cxsf, lr
- ldmia sp, {r0 - pc}^ @ Restore SVC registers
+ ldr r2, [sp, #S_PSR] @ Get SVC cpsr
+ svc_exit r2 @ return from exception
UNWIND(.fnend )
ENDPROC(__und_svc)
@@ -323,9 +329,8 @@ __pabt_svc:
@
@ restore SPSR and restart the instruction
@
- ldr r0, [sp, #S_PSR]
- msr spsr_cxsf, r0
- ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
+ ldr r2, [sp, #S_PSR]
+ svc_exit r2 @ return from exception
UNWIND(.fnend )
ENDPROC(__pabt_svc)
@@ -353,7 +358,8 @@ ENDPROC(__pabt_svc)
UNWIND(.fnstart )
UNWIND(.cantunwind ) @ don't unwind the user space
sub sp, sp, #S_FRAME_SIZE
- stmib sp, {r1 - r12}
+ ARM( stmib sp, {r1 - r12} )
+ THUMB( stmia sp, {r0 - r12} )
ldmia r0, {r1 - r3}
add r0, sp, #S_PC @ here for interlock avoidance
@@ -372,7 +378,8 @@ ENDPROC(__pabt_svc)
@ Also, separately save sp_usr and lr_usr
@
stmia r0, {r2 - r4}
- stmdb r0, {sp, lr}^
+ ARM( stmdb r0, {sp, lr}^ )
+ THUMB( store_user_sp_lr r0, r1, S_SP - S_PC )
@
@ Enable the alignment trap while in kernel mode
@@ -383,6 +390,8 @@ ENDPROC(__pabt_svc)
@ Clear FP to mark the first stack frame
@
zero_fp
+
+ asm_trace_hardirqs_off
.endm
.macro kuser_cmpxchg_check
@@ -427,7 +436,7 @@ __dabt_usr:
@
enable_irq
mov r2, sp
- adr lr, ret_from_exception
+ adr lr, BSYM(ret_from_exception)
b do_DataAbort
UNWIND(.fnend )
ENDPROC(__dabt_usr)
@@ -437,9 +446,6 @@ __irq_usr:
usr_entry
kuser_cmpxchg_check
-#ifdef CONFIG_TRACE_IRQFLAGS
- bl trace_hardirqs_off
-#endif
get_thread_info tsk
#ifdef CONFIG_PREEMPT
ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
@@ -452,7 +458,9 @@ __irq_usr:
ldr r0, [tsk, #TI_PREEMPT]
str r8, [tsk, #TI_PREEMPT]
teq r0, r7
- strne r0, [r0, -r0]
+ ARM( strne r0, [r0, -r0] )
+ THUMB( movne r0, #0 )
+ THUMB( strne r0, [r0] )
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
bl trace_hardirqs_on
@@ -476,9 +484,10 @@ __und_usr:
@
@ r0 - instruction
@
- adr r9, ret_from_exception
- adr lr, __und_usr_unknown
+ adr r9, BSYM(ret_from_exception)
+ adr lr, BSYM(__und_usr_unknown)
tst r3, #PSR_T_BIT @ Thumb mode?
+ itet eq @ explicit IT needed for the 1f label
subeq r4, r2, #4 @ ARM instr at LR - 4
subne r4, r2, #2 @ Thumb instr at LR - 2
1: ldreqt r0, [r4]
@@ -488,7 +497,10 @@ __und_usr:
beq call_fpe
@ Thumb instruction
#if __LINUX_ARM_ARCH__ >= 7
-2: ldrht r5, [r4], #2
+2:
+ ARM( ldrht r5, [r4], #2 )
+ THUMB( ldrht r5, [r4] )
+ THUMB( add r4, r4, #2 )
and r0, r5, #0xf800 @ mask bits 111x x... .... ....
cmp r0, #0xe800 @ 32bit instruction if xx != 0
blo __und_usr_unknown
@@ -577,9 +589,11 @@ call_fpe:
moveq pc, lr
get_thread_info r10 @ get current thread
and r8, r0, #0x00000f00 @ mask out CP number
+ THUMB( lsr r8, r8, #8 )
mov r7, #1
add r6, r10, #TI_USED_CP
- strb r7, [r6, r8, lsr #8] @ set appropriate used_cp[]
+ ARM( strb r7, [r6, r8, lsr #8] ) @ set appropriate used_cp[]
+ THUMB( strb r7, [r6, r8] ) @ set appropriate used_cp[]
#ifdef CONFIG_IWMMXT
@ Test if we need to give access to iWMMXt coprocessors
ldr r5, [r10, #TI_FLAGS]
@@ -587,36 +601,38 @@ call_fpe:
movcss r7, r5, lsr #(TIF_USING_IWMMXT + 1)
bcs iwmmxt_task_enable
#endif
- add pc, pc, r8, lsr #6
- mov r0, r0
-
- mov pc, lr @ CP#0
- b do_fpe @ CP#1 (FPE)
- b do_fpe @ CP#2 (FPE)
- mov pc, lr @ CP#3
+ ARM( add pc, pc, r8, lsr #6 )
+ THUMB( lsl r8, r8, #2 )
+ THUMB( add pc, r8 )
+ nop
+
+ W(mov) pc, lr @ CP#0
+ W(b) do_fpe @ CP#1 (FPE)
+ W(b) do_fpe @ CP#2 (FPE)
+ W(mov) pc, lr @ CP#3
#ifdef CONFIG_CRUNCH
b crunch_task_enable @ CP#4 (MaverickCrunch)
b crunch_task_enable @ CP#5 (MaverickCrunch)
b crunch_task_enable @ CP#6 (MaverickCrunch)
#else
- mov pc, lr @ CP#4
- mov pc, lr @ CP#5
- mov pc, lr @ CP#6
+ W(mov) pc, lr @ CP#4
+ W(mov) pc, lr @ CP#5
+ W(mov) pc, lr @ CP#6
#endif
- mov pc, lr @ CP#7
- mov pc, lr @ CP#8
- mov pc, lr @ CP#9
+ W(mov) pc, lr @ CP#7
+ W(mov) pc, lr @ CP#8
+ W(mov) pc, lr @ CP#9
#ifdef CONFIG_VFP
- b do_vfp @ CP#10 (VFP)
- b do_vfp @ CP#11 (VFP)
+ W(b) do_vfp @ CP#10 (VFP)
+ W(b) do_vfp @ CP#11 (VFP)
#else
- mov pc, lr @ CP#10 (VFP)
- mov pc, lr @ CP#11 (VFP)
+ W(mov) pc, lr @ CP#10 (VFP)
+ W(mov) pc, lr @ CP#11 (VFP)
#endif
- mov pc, lr @ CP#12
- mov pc, lr @ CP#13
- mov pc, lr @ CP#14 (Debug)
- mov pc, lr @ CP#15 (Control)
+ W(mov) pc, lr @ CP#12
+ W(mov) pc, lr @ CP#13
+ W(mov) pc, lr @ CP#14 (Debug)
+ W(mov) pc, lr @ CP#15 (Control)
#ifdef CONFIG_NEON
.align 6
@@ -667,7 +683,7 @@ no_fp: mov pc, lr
__und_usr_unknown:
enable_irq
mov r0, sp
- adr lr, ret_from_exception
+ adr lr, BSYM(ret_from_exception)
b do_undefinstr
ENDPROC(__und_usr_unknown)
@@ -711,7 +727,10 @@ ENTRY(__switch_to)
UNWIND(.cantunwind )
add ip, r1, #TI_CPU_SAVE
ldr r3, [r2, #TI_TP_VALUE]
- stmia ip!, {r4 - sl, fp, sp, lr} @ Store most regs on stack
+ ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack
+ THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack
+ THUMB( str sp, [ip], #4 )
+ THUMB( str lr, [ip], #4 )
#ifdef CONFIG_MMU
ldr r6, [r2, #TI_CPU_DOMAIN]
#endif
@@ -736,8 +755,12 @@ ENTRY(__switch_to)
ldr r0, =thread_notify_head
mov r1, #THREAD_NOTIFY_SWITCH
bl atomic_notifier_call_chain
+ THUMB( mov ip, r4 )
mov r0, r5
- ldmia r4, {r4 - sl, fp, sp, pc} @ Load all regs saved previously
+ ARM( ldmia r4, {r4 - sl, fp, sp, pc} ) @ Load all regs saved previously
+ THUMB( ldmia ip!, {r4 - sl, fp} ) @ Load all regs saved previously
+ THUMB( ldr sp, [ip], #4 )
+ THUMB( ldr pc, [ip] )
UNWIND(.fnend )
ENDPROC(__switch_to)
@@ -772,6 +795,7 @@ ENDPROC(__switch_to)
* if your compiled code is not going to use the new instructions for other
* purpose.
*/
+ THUMB( .arm )
.macro usr_ret, reg
#ifdef CONFIG_ARM_THUMB
@@ -1020,6 +1044,7 @@ __kuser_helper_version: @ 0xffff0ffc
.globl __kuser_helper_end
__kuser_helper_end:
+ THUMB( .thumb )
/*
* Vector stubs.
@@ -1054,17 +1079,23 @@ vector_\name:
@ Prepare for SVC32 mode. IRQs remain disabled.
@
mrs r0, cpsr
- eor r0, r0, #(\mode ^ SVC_MODE)
+ eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
msr spsr_cxsf, r0
@
@ the branch table must immediately follow this code
@
and lr, lr, #0x0f
+ THUMB( adr r0, 1f )
+ THUMB( ldr lr, [r0, lr, lsl #2] )
mov r0, sp
- ldr lr, [pc, lr, lsl #2]
+ ARM( ldr lr, [pc, lr, lsl #2] )
movs pc, lr @ branch to handler in SVC mode
ENDPROC(vector_\name)
+
+ .align 2
+ @ handler addresses follow this label
+1:
.endm
.globl __stubs_start
@@ -1202,14 +1233,16 @@ __stubs_end:
.globl __vectors_start
__vectors_start:
- swi SYS_ERROR0
- b vector_und + stubs_offset
- ldr pc, .LCvswi + stubs_offset
- b vector_pabt + stubs_offset
- b vector_dabt + stubs_offset
- b vector_addrexcptn + stubs_offset
- b vector_irq + stubs_offset
- b vector_fiq + stubs_offset
+ ARM( swi SYS_ERROR0 )
+ THUMB( svc #0 )
+ THUMB( nop )
+ W(b) vector_und + stubs_offset
+ W(ldr) pc, .LCvswi + stubs_offset
+ W(b) vector_pabt + stubs_offset
+ W(b) vector_dabt + stubs_offset
+ W(b) vector_addrexcptn + stubs_offset
+ W(b) vector_irq + stubs_offset
+ W(b) vector_fiq + stubs_offset
.globl __vectors_end
__vectors_end:
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 7813ab782fd..807cfebb0f4 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -33,14 +33,7 @@ ret_fast_syscall:
/* perform architecture specific actions before user return */
arch_ret_to_user r1, lr
- @ fast_restore_user_regs
- ldr r1, [sp, #S_OFF + S_PSR] @ get calling cpsr
- ldr lr, [sp, #S_OFF + S_PC]! @ get pc
- msr spsr_cxsf, r1 @ save in spsr_svc
- ldmdb sp, {r1 - lr}^ @ get calling r1 - lr
- mov r0, r0
- add sp, sp, #S_FRAME_SIZE - S_PC
- movs pc, lr @ return & move spsr_svc into cpsr
+ restore_user_regs fast = 1, offset = S_OFF
UNWIND(.fnend )
/*
@@ -73,14 +66,7 @@ no_work_pending:
/* perform architecture specific actions before user return */
arch_ret_to_user r1, lr
- @ slow_restore_user_regs
- ldr r1, [sp, #S_PSR] @ get calling cpsr
- ldr lr, [sp, #S_PC]! @ get pc
- msr spsr_cxsf, r1 @ save in spsr_svc
- ldmdb sp, {r0 - lr}^ @ get calling r0 - lr
- mov r0, r0
- add sp, sp, #S_FRAME_SIZE - S_PC
- movs pc, lr @ return & move spsr_svc into cpsr
+ restore_user_regs fast = 0, offset = 0
ENDPROC(ret_to_user)
/*
@@ -132,6 +118,25 @@ ftrace_call:
#else
+ENTRY(__gnu_mcount_nc)
+ stmdb sp!, {r0-r3, lr}
+ ldr r0, =ftrace_trace_function
+ ldr r2, [r0]
+ adr r0, ftrace_stub
+ cmp r0, r2
+ bne gnu_trace
+ ldmia sp!, {r0-r3, ip, lr}
+ bx ip
+
+gnu_trace:
+ ldr r1, [sp, #20] @ lr of instrumented routine
+ mov r0, lr
+ sub r0, r0, #MCOUNT_INSN_SIZE
+ mov lr, pc
+ mov pc, r2
+ ldmia sp!, {r0-r3, ip, lr}
+ bx ip
+
ENTRY(mcount)
stmdb sp!, {r0-r3, lr}
ldr r0, =ftrace_trace_function
@@ -182,8 +187,10 @@ ftrace_stub:
ENTRY(vector_swi)
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0 - r12
- add r8, sp, #S_PC
- stmdb r8, {sp, lr}^ @ Calling sp, lr
+ ARM( add r8, sp, #S_PC )
+ ARM( stmdb r8, {sp, lr}^ ) @ Calling sp, lr
+ THUMB( mov r8, sp )
+ THUMB( store_user_sp_lr r8, r10, S_SP ) @ calling sp, lr
mrs r8, spsr @ called from non-FIQ mode, so ok.
str lr, [sp, #S_PC] @ Save calling PC
str r8, [sp, #S_PSR] @ Save CPSR
@@ -272,7 +279,7 @@ ENTRY(vector_swi)
bne __sys_trace
cmp scno, #NR_syscalls @ check upper syscall limit
- adr lr, ret_fast_syscall @ return address
+ adr lr, BSYM(ret_fast_syscall) @ return address
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
add r1, sp, #S_OFF
@@ -293,7 +300,7 @@ __sys_trace:
mov r0, #0 @ trace entry [IP = 0]
bl syscall_trace
- adr lr, __sys_trace_return @ return address
+ adr lr, BSYM(__sys_trace_return) @ return address
mov scno, r0 @ syscall number (possibly new)
add r1, sp, #S_R0 + S_OFF @ pointer to regs
cmp scno, #NR_syscalls @ check upper syscall limit
@@ -373,16 +380,6 @@ sys_clone_wrapper:
b sys_clone
ENDPROC(sys_clone_wrapper)
-sys_sigsuspend_wrapper:
- add r3, sp, #S_OFF
- b sys_sigsuspend
-ENDPROC(sys_sigsuspend_wrapper)
-
-sys_rt_sigsuspend_wrapper:
- add r2, sp, #S_OFF
- b sys_rt_sigsuspend
-ENDPROC(sys_rt_sigsuspend_wrapper)
-
sys_sigreturn_wrapper:
add r0, sp, #S_OFF
b sys_sigreturn
diff --git a/arch/arm/kernel/entry-header.S b/arch/arm/kernel/entry-header.S
index 87ab4e15799..a4eaf4f920c 100644
--- a/arch/arm/kernel/entry-header.S
+++ b/arch/arm/kernel/entry-header.S
@@ -36,11 +36,6 @@
#endif
.endm
- .macro get_thread_info, rd
- mov \rd, sp, lsr #13
- mov \rd, \rd, lsl #13
- .endm
-
.macro alignment_trap, rtemp
#ifdef CONFIG_ALIGNMENT_TRAP
ldr \rtemp, .LCcralign
@@ -49,6 +44,93 @@
#endif
.endm
+ @
+ @ Store/load the USER SP and LR registers by switching to the SYS
+ @ mode. Useful in Thumb-2 mode where "stm/ldm rd, {sp, lr}^" is not
+ @ available. Should only be called from SVC mode
+ @
+ .macro store_user_sp_lr, rd, rtemp, offset = 0
+ mrs \rtemp, cpsr
+ eor \rtemp, \rtemp, #(SVC_MODE ^ SYSTEM_MODE)
+ msr cpsr_c, \rtemp @ switch to the SYS mode
+
+ str sp, [\rd, #\offset] @ save sp_usr
+ str lr, [\rd, #\offset + 4] @ save lr_usr
+
+ eor \rtemp, \rtemp, #(SVC_MODE ^ SYSTEM_MODE)
+ msr cpsr_c, \rtemp @ switch back to the SVC mode
+ .endm
+
+ .macro load_user_sp_lr, rd, rtemp, offset = 0
+ mrs \rtemp, cpsr
+ eor \rtemp, \rtemp, #(SVC_MODE ^ SYSTEM_MODE)
+ msr cpsr_c, \rtemp @ switch to the SYS mode
+
+ ldr sp, [\rd, #\offset] @ load sp_usr
+ ldr lr, [\rd, #\offset + 4] @ load lr_usr
+
+ eor \rtemp, \rtemp, #(SVC_MODE ^ SYSTEM_MODE)
+ msr cpsr_c, \rtemp @ switch back to the SVC mode
+ .endm
+
+#ifndef CONFIG_THUMB2_KERNEL
+ .macro svc_exit, rpsr
+ msr spsr_cxsf, \rpsr
+ ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
+ .endm
+
+ .macro restore_user_regs, fast = 0, offset = 0
+ ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr
+ ldr lr, [sp, #\offset + S_PC]! @ get pc
+ msr spsr_cxsf, r1 @ save in spsr_svc
+ .if \fast
+ ldmdb sp, {r1 - lr}^ @ get calling r1 - lr
+ .else
+ ldmdb sp, {r0 - lr}^ @ get calling r0 - lr
+ .endif
+ add sp, sp, #S_FRAME_SIZE - S_PC
+ movs pc, lr @ return & move spsr_svc into cpsr
+ .endm
+
+ .macro get_thread_info, rd
+ mov \rd, sp, lsr #13
+ mov \rd, \rd, lsl #13
+ .endm
+#else /* CONFIG_THUMB2_KERNEL */
+ .macro svc_exit, rpsr
+ ldr r0, [sp, #S_SP] @ top of the stack
+ ldr r1, [sp, #S_PC] @ return address
+ tst r0, #4 @ orig stack 8-byte aligned?
+ stmdb r0, {r1, \rpsr} @ rfe context
+ ldmia sp, {r0 - r12}
+ ldr lr, [sp, #S_LR]
+ addeq sp, sp, #S_FRAME_SIZE - 8 @ aligned
+ addne sp, sp, #S_FRAME_SIZE - 4 @ not aligned
+ rfeia sp!
+ .endm
+
+ .macro restore_user_regs, fast = 0, offset = 0
+ mov r2, sp
+ load_user_sp_lr r2, r3, \offset + S_SP @ calling sp, lr
+ ldr r1, [sp, #\offset + S_PSR] @ get calling cpsr
+ ldr lr, [sp, #\offset + S_PC] @ get pc
+ add sp, sp, #\offset + S_SP
+ msr spsr_cxsf, r1 @ save in spsr_svc
+ .if \fast
+ ldmdb sp, {r1 - r12} @ get calling r1 - r12
+ .else
+ ldmdb sp, {r0 - r12} @ get calling r0 - r12
+ .endif
+ add sp, sp, #S_FRAME_SIZE - S_SP
+ movs pc, lr @ return & move spsr_svc into cpsr
+ .endm
+
+ .macro get_thread_info, rd
+ mov \rd, sp
+ lsr \rd, \rd, #13
+ mov \rd, \rd, lsl #13
+ .endm
+#endif /* !CONFIG_THUMB2_KERNEL */
/*
* These are the registers used in the syscall handler, and allow us to
diff --git a/arch/arm/kernel/head-common.S b/arch/arm/kernel/head-common.S
index 991952c644d..93ad576b2d7 100644
--- a/arch/arm/kernel/head-common.S
+++ b/arch/arm/kernel/head-common.S
@@ -14,6 +14,7 @@
#define ATAG_CORE 0x54410001
#define ATAG_CORE_SIZE ((2*4 + 3*4) >> 2)
+ .align 2
.type __switch_data, %object
__switch_data:
.long __mmap_switched
@@ -51,7 +52,9 @@ __mmap_switched:
strcc fp, [r6],#4
bcc 1b
- ldmia r3, {r4, r5, r6, r7, sp}
+ ARM( ldmia r3, {r4, r5, r6, r7, sp})
+ THUMB( ldmia r3, {r4, r5, r6, r7} )
+ THUMB( ldr sp, [r3, #16] )
str r9, [r4] @ Save processor ID
str r1, [r5] @ Save machine type
str r2, [r6] @ Save atags pointer
@@ -155,7 +158,8 @@ ENDPROC(__error)
*/
__lookup_processor_type:
adr r3, 3f
- ldmda r3, {r5 - r7}
+ ldmia r3, {r5 - r7}
+ add r3, r3, #8
sub r3, r3, r7 @ get offset between virt&phys
add r5, r5, r3 @ convert virt addresses to
add r6, r6, r3 @ physical address space
@@ -185,9 +189,10 @@ ENDPROC(lookup_processor_type)
* Look in <asm/procinfo.h> and arch/arm/kernel/arch.[ch] for
* more information about the __proc_info and __arch_info structures.
*/
- .long __proc_info_begin
+ .align 2
+3: .long __proc_info_begin
.long __proc_info_end
-3: .long .
+4: .long .
.long __arch_info_begin
.long __arch_info_end
@@ -203,7 +208,7 @@ ENDPROC(lookup_processor_type)
* r5 = mach_info pointer in physical address space
*/
__lookup_machine_type:
- adr r3, 3b
+ adr r3, 4b
ldmia r3, {r4, r5, r6}
sub r3, r3, r4 @ get offset between virt&phys
add r5, r5, r3 @ convert virt addresses to
diff --git a/arch/arm/kernel/head-nommu.S b/arch/arm/kernel/head-nommu.S
index cc87e1765ed..e5dfc2895e2 100644
--- a/arch/arm/kernel/head-nommu.S
+++ b/arch/arm/kernel/head-nommu.S
@@ -34,7 +34,7 @@
*/
.section ".text.head", "ax"
ENTRY(stext)
- msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
+ setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
@ and irqs disabled
#ifndef CONFIG_CPU_CP15
ldr r9, =CONFIG_PROCESSOR_ID
@@ -50,8 +50,10 @@ ENTRY(stext)
ldr r13, __switch_data @ address to jump to after
@ the initialization is done
- adr lr, __after_proc_init @ return (PIC) address
- add pc, r10, #PROCINFO_INITFUNC
+ adr lr, BSYM(__after_proc_init) @ return (PIC) address
+ ARM( add pc, r10, #PROCINFO_INITFUNC )
+ THUMB( add r12, r10, #PROCINFO_INITFUNC )
+ THUMB( mov pc, r12 )
ENDPROC(stext)
/*
@@ -59,7 +61,10 @@ ENDPROC(stext)
*/
__after_proc_init:
#ifdef CONFIG_CPU_CP15
- mrc p15, 0, r0, c1, c0, 0 @ read control reg
+ /*
+ * CP15 system control register value returned in r0 from
+ * the CPU init function.
+ */
#ifdef CONFIG_ALIGNMENT_TRAP
orr r0, r0, #CR_A
#else
@@ -82,7 +87,8 @@ __after_proc_init:
mcr p15, 0, r0, c1, c0, 0 @ write control reg
#endif /* CONFIG_CPU_CP15 */
- mov pc, r13 @ clear the BSS and jump
+ mov r3, r13
+ mov pc, r3 @ clear the BSS and jump
@ to start_kernel
ENDPROC(__after_proc_init)
.ltorg
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 21e17dc94cb..38ccbe1d3b2 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -76,7 +76,7 @@
*/
.section ".text.head", "ax"
ENTRY(stext)
- msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
+ setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 @ ensure svc mode
@ and irqs disabled
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type @ r5=procinfo r9=cpuid
@@ -97,8 +97,10 @@ ENTRY(stext)
*/
ldr r13, __switch_data @ address to jump to after
@ mmu has been enabled
- adr lr, __enable_mmu @ return (PIC) address
- add pc, r10, #PROCINFO_INITFUNC
+ adr lr, BSYM(__enable_mmu) @ return (PIC) address
+ ARM( add pc, r10, #PROCINFO_INITFUNC )
+ THUMB( add r12, r10, #PROCINFO_INITFUNC )
+ THUMB( mov pc, r12 )
ENDPROC(stext)
#if defined(CONFIG_SMP)
@@ -110,7 +112,7 @@ ENTRY(secondary_startup)
* the processor type - there is no need to check the machine type
* as it has already been validated by the primary processor.
*/
- msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
+ setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
mrc p15, 0, r9, c0, c0 @ get processor id
bl __lookup_processor_type
movs r10, r5 @ invalid processor?
@@ -121,12 +123,15 @@ ENTRY(secondary_startup)
* Use the page tables supplied from __cpu_up.
*/
adr r4, __secondary_data
- ldmia r4, {r5, r7, r13} @ address to jump to after
+ ldmia r4, {r5, r7, r12} @ address to jump to after
sub r4, r4, r5 @ mmu has been enabled
ldr r4, [r7, r4] @ get secondary_data.pgdir
- adr lr, __enable_mmu @ return address
- add pc, r10, #PROCINFO_INITFUNC @ initialise processor
- @ (return control reg)
+ adr lr, BSYM(__enable_mmu) @ return address
+ mov r13, r12 @ __secondary_switched address
+ ARM( add pc, r10, #PROCINFO_INITFUNC ) @ initialise processor
+ @ (return control reg)
+ THUMB( add r12, r10, #PROCINFO_INITFUNC )
+ THUMB( mov pc, r12 )
ENDPROC(secondary_startup)
/*
@@ -193,8 +198,8 @@ __turn_mmu_on:
mcr p15, 0, r0, c1, c0, 0 @ write control reg
mrc p15, 0, r3, c0, c0, 0 @ read id reg
mov r3, r3
- mov r3, r3
- mov pc, r13
+ mov r3, r13
+ mov pc, r3
ENDPROC(__turn_mmu_on)
@@ -235,7 +240,8 @@ __create_page_tables:
* will be removed by paging_init(). We use our current program
* counter to determine corresponding section base address.
*/
- mov r6, pc, lsr #20 @ start of kernel section
+ mov r6, pc
+ mov r6, r6, lsr #20 @ start of kernel section
orr r3, r7, r6, lsl #20 @ flags + kernel base
str r3, [r4, r6, lsl #2] @ identity mapping
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index b7c3490eaa2..c9a8619f385 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -86,7 +86,7 @@ int show_interrupts(struct seq_file *p, void *v)
unlock:
spin_unlock_irqrestore(&irq_desc[i].lock, flags);
} else if (i == NR_IRQS) {
-#ifdef CONFIG_ARCH_ACORN
+#ifdef CONFIG_FIQ
show_fiq_list(p, v);
#endif
#ifdef CONFIG_SMP
diff --git a/arch/arm/kernel/module.c b/arch/arm/kernel/module.c
index bac03c81489..f28c5e9c51e 100644
--- a/arch/arm/kernel/module.c
+++ b/arch/arm/kernel/module.c
@@ -102,6 +102,7 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
unsigned long loc;
Elf32_Sym *sym;
s32 offset;
+ u32 upper, lower, sign, j1, j2;
offset = ELF32_R_SYM(rel->r_info);
if (offset < 0 || offset > (symsec->sh_size / sizeof(Elf32_Sym))) {
@@ -184,6 +185,58 @@ apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
(offset & 0x0fff);
break;
+ case R_ARM_THM_CALL:
+ case R_ARM_THM_JUMP24:
+ upper = *(u16 *)loc;
+ lower = *(u16 *)(loc + 2);
+
+ /*
+ * 25 bit signed address range (Thumb-2 BL and B.W
+ * instructions):
+ * S:I1:I2:imm10:imm11:0
+ * where:
+ * S = upper[10] = offset[24]
+ * I1 = ~(J1 ^ S) = offset[23]
+ * I2 = ~(J2 ^ S) = offset[22]
+ * imm10 = upper[9:0] = offset[21:12]
+ * imm11 = lower[10:0] = offset[11:1]
+ * J1 = lower[13]
+ * J2 = lower[11]
+ */
+ sign = (upper >> 10) & 1;
+ j1 = (lower >> 13) & 1;
+ j2 = (lower >> 11) & 1;
+ offset = (sign << 24) | ((~(j1 ^ sign) & 1) << 23) |
+ ((~(j2 ^ sign) & 1) << 22) |
+ ((upper & 0x03ff) << 12) |
+ ((lower & 0x07ff) << 1);
+ if (offset & 0x01000000)
+ offset -= 0x02000000;
+ offset += sym->st_value - loc;
+
+ /* only Thumb addresses allowed (no interworking) */
+ if (!(offset & 1) ||
+ offset <= (s32)0xff000000 ||
+ offset >= (s32)0x01000000) {
+ printk(KERN_ERR
+ "%s: relocation out of range, section "
+ "%d reloc %d sym '%s'\n", module->name,
+ relindex, i, strtab + sym->st_name);
+ return -ENOEXEC;
+ }
+
+ sign = (offset >> 24) & 1;
+ j1 = sign ^ (~(offset >> 23) & 1);
+ j2 = sign ^ (~(offset >> 22) & 1);
+ *(u16 *)loc = (u16)((upper & 0xf800) | (sign << 10) |
+ ((offset >> 12) & 0x03ff));
+ *(u16 *)(loc + 2) = (u16)((lower & 0xd000) |
+ (j1 << 13) | (j2 << 11) |
+ ((offset >> 1) & 0x07ff));
+ upper = *(u16 *)loc;
+ lower = *(u16 *)(loc + 2);
+ break;
+
default:
printk(KERN_ERR "%s: unknown relocation: %u\n",
module->name, ELF32_R_TYPE(rel->r_info));
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 39196dff478..790fbee92ec 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -388,7 +388,7 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
regs.ARM_r2 = (unsigned long)fn;
regs.ARM_r3 = (unsigned long)kernel_thread_exit;
regs.ARM_pc = (unsigned long)kernel_thread_helper;
- regs.ARM_cpsr = SVC_MODE | PSR_ENDSTATE;
+ regs.ARM_cpsr = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE;
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
}
diff --git a/arch/arm/kernel/ptrace.c b/arch/arm/kernel/ptrace.c
index 89882a1d018..a2ea3854cb3 100644
--- a/arch/arm/kernel/ptrace.c
+++ b/arch/arm/kernel/ptrace.c
@@ -521,7 +521,13 @@ static int ptrace_read_user(struct task_struct *tsk, unsigned long off,
return -EIO;
tmp = 0;
- if (off < sizeof(struct pt_regs))
+ if (off == PT_TEXT_ADDR)
+ tmp = tsk->mm->start_code;
+ else if (off == PT_DATA_ADDR)
+ tmp = tsk->mm->start_data;
+ else if (off == PT_TEXT_END_ADDR)
+ tmp = tsk->mm->end_code;
+ else if (off < sizeof(struct pt_regs))
tmp = get_user_reg(tsk, off >> 2);
return put_user(tmp, ret);
diff --git a/arch/arm/kernel/return_address.c b/arch/arm/kernel/return_address.c
new file mode 100644
index 00000000000..df246da4cec
--- /dev/null
+++ b/arch/arm/kernel/return_address.c
@@ -0,0 +1,71 @@
+/*
+ * arch/arm/kernel/return_address.c
+ *
+ * Copyright (C) 2009 Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>
+ * for Pengutronix
+ *
+ * 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/module.h>
+
+#if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND)
+#include <linux/sched.h>
+
+#include <asm/stacktrace.h>
+
+struct return_address_data {
+ unsigned int level;
+ void *addr;
+};
+
+static int save_return_addr(struct stackframe *frame, void *d)
+{
+ struct return_address_data *data = d;
+
+ if (!data->level) {
+ data->addr = (void *)frame->lr;
+
+ return 1;
+ } else {
+ --data->level;
+ return 0;
+ }
+}
+
+void *return_address(unsigned int level)
+{
+ struct return_address_data data;
+ struct stackframe frame;
+ register unsigned long current_sp asm ("sp");
+
+ data.level = level + 1;
+
+ frame.fp = (unsigned long)__builtin_frame_address(0);
+ frame.sp = current_sp;
+ frame.lr = (unsigned long)__builtin_return_address(0);
+ frame.pc = (unsigned long)return_address;
+
+ walk_stackframe(&frame, save_return_addr, &data);
+
+ if (!data.level)
+ return data.addr;
+ else
+ return NULL;
+}
+
+#else /* if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) */
+
+#if defined(CONFIG_ARM_UNWIND)
+#warning "TODO: return_address should use unwind tables"
+#endif
+
+void *return_address(unsigned int level)
+{
+ return NULL;
+}
+
+#endif /* if defined(CONFIG_FRAME_POINTER) && !defined(CONFIG_ARM_UNWIND) / else */
+
+EXPORT_SYMBOL_GPL(return_address);
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index bc5e4128f9f..d4d4f77c91b 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -25,6 +25,7 @@
#include <linux/smp.h>
#include <linux/fs.h>
+#include <asm/unified.h>
#include <asm/cpu.h>
#include <asm/cputype.h>
#include <asm/elf.h>
@@ -327,25 +328,38 @@ void cpu_init(void)
}
/*
+ * Define the placement constraint for the inline asm directive below.
+ * In Thumb-2, msr with an immediate value is not allowed.
+ */
+#ifdef CONFIG_THUMB2_KERNEL
+#define PLC "r"
+#else
+#define PLC "I"
+#endif
+
+ /*
* setup stacks for re-entrant exception handlers
*/
__asm__ (
"msr cpsr_c, %1\n\t"
- "add sp, %0, %2\n\t"
+ "add r14, %0, %2\n\t"
+ "mov sp, r14\n\t"
"msr cpsr_c, %3\n\t"
- "add sp, %0, %4\n\t"
+ "add r14, %0, %4\n\t"
+ "mov sp, r14\n\t"
"msr cpsr_c, %5\n\t"
- "add sp, %0, %6\n\t"
+ "add r14, %0, %6\n\t"
+ "mov sp, r14\n\t"
"msr cpsr_c, %7"
:
: "r" (stk),
- "I" (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
+ PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
"I" (offsetof(struct stack, irq[0])),
- "I" (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
+ PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
"I" (offsetof(struct stack, abt[0])),
- "I" (PSR_F_BIT | PSR_I_BIT | UND_MODE),
+ PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),
"I" (offsetof(struct stack, und[0])),
- "I" (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
+ PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE)
: "r14");
}
diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index b76fe06d92e..1423a341978 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -48,57 +48,22 @@ const unsigned long sigreturn_codes[7] = {
MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN,
};
-static int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall);
-
/*
* atomically swap in the new signal mask, and wait for a signal.
*/
-asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask, struct pt_regs *regs)
+asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, old_sigset_t mask)
{
- sigset_t saveset;
-
mask &= _BLOCKABLE;
spin_lock_irq(&current->sighand->siglock);
- saveset = current->blocked;
+ current->saved_sigmask = current->blocked;
siginitset(&current->blocked, mask);
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
- regs->ARM_r0 = -EINTR;
-
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- if (do_signal(&saveset, regs, 0))
- return regs->ARM_r0;
- }
-}
-
-asmlinkage int
-sys_rt_sigsuspend(sigset_t __user *unewset, size_t sigsetsize, struct pt_regs *regs)
-{
- sigset_t saveset, newset;
-
- /* XXX: Don't preclude handling different sized sigset_t's. */
- if (sigsetsize != sizeof(sigset_t))
- return -EINVAL;
-
- if (copy_from_user(&newset, unewset, sizeof(newset)))
- return -EFAULT;
- sigdelsetmask(&newset, ~_BLOCKABLE);
-
- spin_lock_irq(&current->sighand->siglock);
- saveset = current->blocked;
- current->blocked = newset;
- recalc_sigpending();
- spin_unlock_irq(&current->sighand->siglock);
- regs->ARM_r0 = -EINTR;
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- if (do_signal(&saveset, regs, 0))
- return regs->ARM_r0;
- }
+ current->state = TASK_INTERRUPTIBLE;
+ schedule();
+ set_restore_sigmask();
+ return -ERESTARTNOHAND;
}
asmlinkage int
@@ -546,7 +511,7 @@ static inline void setup_syscall_restart(struct pt_regs *regs)
/*
* OK, we're invoking a handler
*/
-static void
+static int
handle_signal(unsigned long sig, struct k_sigaction *ka,
siginfo_t *info, sigset_t *oldset,
struct pt_regs * regs, int syscall)
@@ -597,7 +562,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
if (ret != 0) {
force_sigsegv(sig, tsk);
- return;
+ return ret;
}
/*
@@ -611,6 +576,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
recalc_sigpending();
spin_unlock_irq(&tsk->sighand->siglock);
+ return 0;
}
/*
@@ -622,7 +588,7 @@ handle_signal(unsigned long sig, struct k_sigaction *ka,
* the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that.
*/
-static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
+static void do_signal(struct pt_regs *regs, int syscall)
{
struct k_sigaction ka;
siginfo_t info;
@@ -635,7 +601,7 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
* if so.
*/
if (!user_mode(regs))
- return 0;
+ return;
if (try_to_freeze())
goto no_signal;
@@ -644,9 +610,24 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) {
- handle_signal(signr, &ka, &info, oldset, regs, syscall);
+ sigset_t *oldset;
+
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ oldset = &current->saved_sigmask;
+ else
+ oldset = &current->blocked;
+ if (handle_signal(signr, &ka, &info, oldset, regs, syscall) == 0) {
+ /*
+ * A signal was successfully delivered; the saved
+ * sigmask will have been stored in the signal frame,
+ * and will be restored by sigreturn, so we can simply
+ * clear the TIF_RESTORE_SIGMASK flag.
+ */
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
+ }
single_step_set(current);
- return 1;
+ return;
}
no_signal:
@@ -698,16 +679,23 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
regs->ARM_r0 == -ERESTARTNOINTR) {
setup_syscall_restart(regs);
}
+
+ /* If there's no signal to deliver, we just put the saved sigmask
+ * back.
+ */
+ if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
+ sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
+ }
}
single_step_set(current);
- return 0;
}
asmlinkage void
do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
{
if (thread_flags & _TIF_SIGPENDING)
- do_signal(&current->blocked, regs, syscall);
+ do_signal(regs, syscall);
if (thread_flags & _TIF_NOTIFY_RESUME) {
clear_thread_flag(TIF_NOTIFY_RESUME);
diff --git a/arch/arm/kernel/stacktrace.c b/arch/arm/kernel/stacktrace.c
index 9f444e5cc16..20b7411e47f 100644
--- a/arch/arm/kernel/stacktrace.c
+++ b/arch/arm/kernel/stacktrace.c
@@ -21,7 +21,7 @@
* Note that with framepointer enabled, even the leaf functions have the same
* prologue and epilogue, therefore we can ignore the LR value in this case.
*/
-int unwind_frame(struct stackframe *frame)
+int notrace unwind_frame(struct stackframe *frame)
{
unsigned long high, low;
unsigned long fp = frame->fp;
@@ -43,7 +43,7 @@ int unwind_frame(struct stackframe *frame)
}
#endif
-void walk_stackframe(struct stackframe *frame,
+void notrace walk_stackframe(struct stackframe *frame,
int (*fn)(struct stackframe *, void *), void *data)
{
while (1) {
diff --git a/arch/arm/kernel/unwind.c b/arch/arm/kernel/unwind.c
index dd56e11f339..39baf1128bf 100644
--- a/arch/arm/kernel/unwind.c
+++ b/arch/arm/kernel/unwind.c
@@ -62,7 +62,11 @@ struct unwind_ctrl_block {
};
enum regs {
+#ifdef CONFIG_THUMB2_KERNEL
+ FP = 7,
+#else
FP = 11,
+#endif
SP = 13,
LR = 14,
PC = 15
diff --git a/arch/arm/lib/ashldi3.S b/arch/arm/lib/ashldi3.S
index 1154d924080..638deb13da1 100644
--- a/arch/arm/lib/ashldi3.S
+++ b/arch/arm/lib/ashldi3.S
@@ -43,7 +43,9 @@ ENTRY(__aeabi_llsl)
rsb ip, r2, #32
movmi ah, ah, lsl r2
movpl ah, al, lsl r3
- orrmi ah, ah, al, lsr ip
+ ARM( orrmi ah, ah, al, lsr ip )
+ THUMB( lsrmi r3, al, ip )
+ THUMB( orrmi ah, ah, r3 )
mov al, al, lsl r2
mov pc, lr
diff --git a/arch/arm/lib/ashrdi3.S b/arch/arm/lib/ashrdi3.S
index 9f8b35572f8..015e8aa5a1d 100644
--- a/arch/arm/lib/ashrdi3.S
+++ b/arch/arm/lib/ashrdi3.S
@@ -43,7 +43,9 @@ ENTRY(__aeabi_lasr)
rsb ip, r2, #32
movmi al, al, lsr r2
movpl al, ah, asr r3
- orrmi al, al, ah, lsl ip
+ ARM( orrmi al, al, ah, lsl ip )
+ THUMB( lslmi r3, ah, ip )
+ THUMB( orrmi al, al, r3 )
mov ah, ah, asr r2
mov pc, lr
diff --git a/arch/arm/lib/backtrace.S b/arch/arm/lib/backtrace.S
index b0951d0e8b2..aaf7220d9e3 100644
--- a/arch/arm/lib/backtrace.S
+++ b/arch/arm/lib/backtrace.S
@@ -38,7 +38,9 @@ ENDPROC(c_backtrace)
beq no_frame @ we have no stack frames
tst r1, #0x10 @ 26 or 32-bit mode?
- moveq mask, #0xfc000003 @ mask for 26-bit
+ ARM( moveq mask, #0xfc000003 )
+ THUMB( moveq mask, #0xfc000000 )
+ THUMB( orreq mask, #0x03 )
movne mask, #0 @ mask for 32-bit
1: stmfd sp!, {pc} @ calculate offset of PC stored
@@ -126,7 +128,9 @@ ENDPROC(c_backtrace)
mov reg, #10
mov r7, #0
1: mov r3, #1
- tst instr, r3, lsl reg
+ ARM( tst instr, r3, lsl reg )
+ THUMB( lsl r3, reg )
+ THUMB( tst instr, r3 )
beq 2f
add r7, r7, #1
teq r7, #6
diff --git a/arch/arm/lib/bitops.h b/arch/arm/lib/bitops.h
index c7f2627385e..d42252918bf 100644
--- a/arch/arm/lib/bitops.h
+++ b/arch/arm/lib/bitops.h
@@ -60,8 +60,8 @@
tst r2, r0, lsl r3
\instr r2, r2, r0, lsl r3
\store r2, [r1]
- restore_irqs ip
moveq r0, #0
+ restore_irqs ip
mov pc, lr
.endm
#endif
diff --git a/arch/arm/lib/clear_user.S b/arch/arm/lib/clear_user.S
index 844f56785eb..1279abd8b88 100644
--- a/arch/arm/lib/clear_user.S
+++ b/arch/arm/lib/clear_user.S
@@ -27,21 +27,20 @@ WEAK(__clear_user)
ands ip, r0, #3
beq 1f
cmp ip, #2
-USER( strbt r2, [r0], #1)
-USER( strlebt r2, [r0], #1)
-USER( strltbt r2, [r0], #1)
+ strusr r2, r0, 1
+ strusr r2, r0, 1, le
+ strusr r2, r0, 1, lt
rsb ip, ip, #4
sub r1, r1, ip @ 7 6 5 4 3 2 1
1: subs r1, r1, #8 @ -1 -2 -3 -4 -5 -6 -7
-USER( strplt r2, [r0], #4)
-USER( strplt r2, [r0], #4)
+ strusr r2, r0, 4, pl, rept=2
bpl 1b
adds r1, r1, #4 @ 3 2 1 0 -1 -2 -3
-USER( strplt r2, [r0], #4)
+ strusr r2, r0, 4, pl
2: tst r1, #2 @ 1x 1x 0x 0x 1x 1x 0x
-USER( strnebt r2, [r0], #1)
-USER( strnebt r2, [r0], #1)
+ strusr r2, r0, 1, ne, rept=2
tst r1, #1 @ x1 x0 x1 x0 x1 x0 x1
+ it ne @ explicit IT needed for the label
USER( strnebt r2, [r0])
mov r0, #0
ldmfd sp!, {r1, pc}
diff --git a/arch/arm/lib/copy_from_user.S b/arch/arm/lib/copy_from_user.S
index 56799a165cc..e4fe124aced 100644
--- a/arch/arm/lib/copy_from_user.S
+++ b/arch/arm/lib/copy_from_user.S
@@ -33,11 +33,15 @@
* Number of bytes NOT copied.
*/
+#ifndef CONFIG_THUMB2_KERNEL
+#define LDR1W_SHIFT 0
+#else
+#define LDR1W_SHIFT 1
+#endif
+#define STR1W_SHIFT 0
+
.macro ldr1w ptr reg abort
-100: ldrt \reg, [\ptr], #4
- .section __ex_table, "a"
- .long 100b, \abort
- .previous
+ ldrusr \reg, \ptr, 4, abort=\abort
.endm
.macro ldr4w ptr reg1 reg2 reg3 reg4 abort
@@ -53,14 +57,11 @@
.endm
.macro ldr1b ptr reg cond=al abort
-100: ldr\cond\()bt \reg, [\ptr], #1
- .section __ex_table, "a"
- .long 100b, \abort
- .previous
+ ldrusr \reg, \ptr, 1, \cond, abort=\abort
.endm
.macro str1w ptr reg abort
- str \reg, [\ptr], #4
+ W(str) \reg, [\ptr], #4
.endm
.macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
diff --git a/arch/arm/lib/copy_template.S b/arch/arm/lib/copy_template.S
index 139cce64605..805e3f8fb00 100644
--- a/arch/arm/lib/copy_template.S
+++ b/arch/arm/lib/copy_template.S
@@ -57,6 +57,13 @@
*
* Restore registers with the values previously saved with the
* 'preserv' macro. Called upon code termination.
+ *
+ * LDR1W_SHIFT
+ * STR1W_SHIFT
+ *
+ * Correction to be applied to the "ip" register when branching into
+ * the ldr1w or str1w instructions (some of these macros may expand to
+ * than one 32bit instruction in Thumb-2)
*/
@@ -99,9 +106,15 @@
5: ands ip, r2, #28
rsb ip, ip, #32
+#if LDR1W_SHIFT > 0
+ lsl ip, ip, #LDR1W_SHIFT
+#endif
addne pc, pc, ip @ C is always clear here
b 7f
-6: nop
+6:
+ .rept (1 << LDR1W_SHIFT)
+ W(nop)
+ .endr
ldr1w r1, r3, abort=20f
ldr1w r1, r4, abort=20f
ldr1w r1, r5, abort=20f
@@ -110,9 +123,16 @@
ldr1w r1, r8, abort=20f
ldr1w r1, lr, abort=20f
+#if LDR1W_SHIFT < STR1W_SHIFT
+ lsl ip, ip, #STR1W_SHIFT - LDR1W_SHIFT
+#elif LDR1W_SHIFT > STR1W_SHIFT
+ lsr ip, ip, #LDR1W_SHIFT - STR1W_SHIFT
+#endif
add pc, pc, ip
nop
- nop
+ .rept (1 << STR1W_SHIFT)
+ W(nop)
+ .endr
str1w r0, r3, abort=20f
str1w r0, r4, abort=20f
str1w r0, r5, abort=20f
diff --git a/arch/arm/lib/copy_to_user.S b/arch/arm/lib/copy_to_user.S
index 878820f0a32..1a71e158444 100644
--- a/arch/arm/lib/copy_to_user.S
+++ b/arch/arm/lib/copy_to_user.S
@@ -33,8 +33,15 @@
* Number of bytes NOT copied.
*/
+#define LDR1W_SHIFT 0
+#ifndef CONFIG_THUMB2_KERNEL
+#define STR1W_SHIFT 0
+#else
+#define STR1W_SHIFT 1
+#endif
+
.macro ldr1w ptr reg abort
- ldr \reg, [\ptr], #4
+ W(ldr) \reg, [\ptr], #4
.endm
.macro ldr4w ptr reg1 reg2 reg3 reg4 abort
@@ -50,10 +57,7 @@
.endm
.macro str1w ptr reg abort
-100: strt \reg, [\ptr], #4
- .section __ex_table, "a"
- .long 100b, \abort
- .previous
+ strusr \reg, \ptr, 4, abort=\abort
.endm
.macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
@@ -68,10 +72,7 @@
.endm
.macro str1b ptr reg cond=al abort
-100: str\cond\()bt \reg, [\ptr], #1
- .section __ex_table, "a"
- .long 100b, \abort
- .previous
+ strusr \reg, \ptr, 1, \cond, abort=\abort
.endm
.macro enter reg1 reg2
diff --git a/arch/arm/lib/csumpartialcopyuser.S b/arch/arm/lib/csumpartialcopyuser.S
index 14677fb4b0c..fd0e9dcd9fd 100644
--- a/arch/arm/lib/csumpartialcopyuser.S
+++ b/arch/arm/lib/csumpartialcopyuser.S
@@ -26,50 +26,28 @@
.endm
.macro load1b, reg1
-9999: ldrbt \reg1, [r0], $1
- .section __ex_table, "a"
- .align 3
- .long 9999b, 6001f
- .previous
+ ldrusr \reg1, r0, 1
.endm
.macro load2b, reg1, reg2
-9999: ldrbt \reg1, [r0], $1
-9998: ldrbt \reg2, [r0], $1
- .section __ex_table, "a"
- .long 9999b, 6001f
- .long 9998b, 6001f
- .previous
+ ldrusr \reg1, r0, 1
+ ldrusr \reg2, r0, 1
.endm
.macro load1l, reg1
-9999: ldrt \reg1, [r0], $4
- .section __ex_table, "a"
- .align 3
- .long 9999b, 6001f
- .previous
+ ldrusr \reg1, r0, 4
.endm
.macro load2l, reg1, reg2
-9999: ldrt \reg1, [r0], $4
-9998: ldrt \reg2, [r0], $4
- .section __ex_table, "a"
- .long 9999b, 6001f
- .long 9998b, 6001f
- .previous
+ ldrusr \reg1, r0, 4
+ ldrusr \reg2, r0, 4
.endm
.macro load4l, reg1, reg2, reg3, reg4
-9999: ldrt \reg1, [r0], $4
-9998: ldrt \reg2, [r0], $4
-9997: ldrt \reg3, [r0], $4
-9996: ldrt \reg4, [r0], $4
- .section __ex_table, "a"
- .long 9999b, 6001f
- .long 9998b, 6001f
- .long 9997b, 6001f
- .long 9996b, 6001f
- .previous
+ ldrusr \reg1, r0, 4
+ ldrusr \reg2, r0, 4
+ ldrusr \reg3, r0, 4
+ ldrusr \reg4, r0, 4
.endm
/*
@@ -92,14 +70,14 @@
*/
.section .fixup,"ax"
.align 4
-6001: mov r4, #-EFAULT
+9001: mov r4, #-EFAULT
ldr r5, [fp, #4] @ *err_ptr
str r4, [r5]
ldmia sp, {r1, r2} @ retrieve dst, len
add r2, r2, r1
mov r0, #0 @ zero the buffer
-6002: teq r2, r1
+9002: teq r2, r1
strneb r0, [r1], #1
- bne 6002b
+ bne 9002b
load_regs
.previous
diff --git a/arch/arm/lib/div64.S b/arch/arm/lib/div64.S
index 1425e789ba8..faa7748142d 100644
--- a/arch/arm/lib/div64.S
+++ b/arch/arm/lib/div64.S
@@ -177,7 +177,9 @@ ENTRY(__do_div64)
mov yh, xh, lsr ip
mov yl, xl, lsr ip
rsb ip, ip, #32
- orr yl, yl, xh, lsl ip
+ ARM( orr yl, yl, xh, lsl ip )
+ THUMB( lsl xh, xh, ip )
+ THUMB( orr yl, yl, xh )
mov xh, xl, lsl ip
mov xh, xh, lsr ip
mov pc, lr
diff --git a/arch/arm/lib/findbit.S b/arch/arm/lib/findbit.S
index 8c4defc4f3c..1e4cbd4e7be 100644
--- a/arch/arm/lib/findbit.S
+++ b/arch/arm/lib/findbit.S
@@ -25,7 +25,10 @@ ENTRY(_find_first_zero_bit_le)
teq r1, #0
beq 3f
mov r2, #0
-1: ldrb r3, [r0, r2, lsr #3]
+1:
+ ARM( ldrb r3, [r0, r2, lsr #3] )
+ THUMB( lsr r3, r2, #3 )
+ THUMB( ldrb r3, [r0, r3] )
eors r3, r3, #0xff @ invert bits
bne .L_found @ any now set - found zero bit
add r2, r2, #8 @ next bit pointer
@@ -44,7 +47,9 @@ ENTRY(_find_next_zero_bit_le)
beq 3b
ands ip, r2, #7
beq 1b @ If new byte, goto old routine
- ldrb r3, [r0, r2, lsr #3]
+ ARM( ldrb r3, [r0, r2, lsr #3] )
+ THUMB( lsr r3, r2, #3 )
+ THUMB( ldrb r3, [r0, r3] )
eor r3, r3, #0xff @ now looking for a 1 bit
movs r3, r3, lsr ip @ shift off unused bits
bne .L_found
@@ -61,7 +66,10 @@ ENTRY(_find_first_bit_le)
teq r1, #0
beq 3f
mov r2, #0
-1: ldrb r3, [r0, r2, lsr #3]
+1:
+ ARM( ldrb r3, [r0, r2, lsr #3] )
+ THUMB( lsr r3, r2, #3 )
+ THUMB( ldrb r3, [r0, r3] )
movs r3, r3
bne .L_found @ any now set - found zero bit
add r2, r2, #8 @ next bit pointer
@@ -80,7 +88,9 @@ ENTRY(_find_next_bit_le)
beq 3b
ands ip, r2, #7
beq 1b @ If new byte, goto old routine
- ldrb r3, [r0, r2, lsr #3]
+ ARM( ldrb r3, [r0, r2, lsr #3] )
+ THUMB( lsr r3, r2, #3 )
+ THUMB( ldrb r3, [r0, r3] )
movs r3, r3, lsr ip @ shift off unused bits
bne .L_found
orr r2, r2, #7 @ if zero, then no bits here
@@ -95,7 +105,9 @@ ENTRY(_find_first_zero_bit_be)
beq 3f
mov r2, #0
1: eor r3, r2, #0x18 @ big endian byte ordering
- ldrb r3, [r0, r3, lsr #3]
+ ARM( ldrb r3, [r0, r3, lsr #3] )
+ THUMB( lsr r3, #3 )
+ THUMB( ldrb r3, [r0, r3] )
eors r3, r3, #0xff @ invert bits
bne .L_found @ any now set - found zero bit
add r2, r2, #8 @ next bit pointer
@@ -111,7 +123,9 @@ ENTRY(_find_next_zero_bit_be)
ands ip, r2, #7
beq 1b @ If new byte, goto old routine
eor r3, r2, #0x18 @ big endian byte ordering
- ldrb r3, [r0, r3, lsr #3]
+ ARM( ldrb r3, [r0, r3, lsr #3] )
+ THUMB( lsr r3, #3 )
+ THUMB( ldrb r3, [r0, r3] )
eor r3, r3, #0xff @ now looking for a 1 bit
movs r3, r3, lsr ip @ shift off unused bits
bne .L_found
@@ -125,7 +139,9 @@ ENTRY(_find_first_bit_be)
beq 3f
mov r2, #0
1: eor r3, r2, #0x18 @ big endian byte ordering
- ldrb r3, [r0, r3, lsr #3]
+ ARM( ldrb r3, [r0, r3, lsr #3] )
+ THUMB( lsr r3, #3 )
+ THUMB( ldrb r3, [r0, r3] )
movs r3, r3
bne .L_found @ any now set - found zero bit
add r2, r2, #8 @ next bit pointer
@@ -141,7 +157,9 @@ ENTRY(_find_next_bit_be)
ands ip, r2, #7
beq 1b @ If new byte, goto old routine
eor r3, r2, #0x18 @ big endian byte ordering
- ldrb r3, [r0, r3, lsr #3]
+ ARM( ldrb r3, [r0, r3, lsr #3] )
+ THUMB( lsr r3, #3 )
+ THUMB( ldrb r3, [r0, r3] )
movs r3, r3, lsr ip @ shift off unused bits
bne .L_found
orr r2, r2, #7 @ if zero, then no bits here
diff --git a/arch/arm/lib/getuser.S b/arch/arm/lib/getuser.S
index 6763088b760..a1814d92712 100644
--- a/arch/arm/lib/getuser.S
+++ b/arch/arm/lib/getuser.S
@@ -36,8 +36,13 @@ ENTRY(__get_user_1)
ENDPROC(__get_user_1)
ENTRY(__get_user_2)
+#ifdef CONFIG_THUMB2_KERNEL
+2: ldrbt r2, [r0]
+3: ldrbt r3, [r0, #1]
+#else
2: ldrbt r2, [r0], #1
3: ldrbt r3, [r0]
+#endif
#ifndef __ARMEB__
orr r2, r2, r3, lsl #8
#else
diff --git a/arch/arm/lib/io-writesw-armv4.S b/arch/arm/lib/io-writesw-armv4.S
index d6585612c86..ff4f71b579e 100644
--- a/arch/arm/lib/io-writesw-armv4.S
+++ b/arch/arm/lib/io-writesw-armv4.S
@@ -75,7 +75,10 @@ ENTRY(__raw_writesw)
#endif
.Loutsw_noalign:
- ldr r3, [r1, -r3]!
+ ARM( ldr r3, [r1, -r3]! )
+ THUMB( rsb r3, r3, #0 )
+ THUMB( ldr r3, [r1, r3] )
+ THUMB( sub r1, r3 )
subcs r2, r2, #1
bcs 2f
subs r2, r2, #2
diff --git a/arch/arm/lib/lshrdi3.S b/arch/arm/lib/lshrdi3.S
index 99ea338bf87..f83d449141f 100644
--- a/arch/arm/lib/lshrdi3.S
+++ b/arch/arm/lib/lshrdi3.S
@@ -43,7 +43,9 @@ ENTRY(__aeabi_llsr)
rsb ip, r2, #32
movmi al, al, lsr r2
movpl al, ah, lsr r3
- orrmi al, al, ah, lsl ip
+ ARM( orrmi al, al, ah, lsl ip )
+ THUMB( lslmi r3, ah, ip )
+ THUMB( orrmi al, al, r3 )
mov ah, ah, lsr r2
mov pc, lr
diff --git a/arch/arm/lib/memcpy.S b/arch/arm/lib/memcpy.S
index e0d002641d3..a9b9e2287a0 100644
--- a/arch/arm/lib/memcpy.S
+++ b/arch/arm/lib/memcpy.S
@@ -13,8 +13,11 @@
#include <linux/linkage.h>
#include <asm/assembler.h>
+#define LDR1W_SHIFT 0
+#define STR1W_SHIFT 0
+
.macro ldr1w ptr reg abort
- ldr \reg, [\ptr], #4
+ W(ldr) \reg, [\ptr], #4
.endm
.macro ldr4w ptr reg1 reg2 reg3 reg4 abort
@@ -30,7 +33,7 @@
.endm
.macro str1w ptr reg abort
- str \reg, [\ptr], #4
+ W(str) \reg, [\ptr], #4
.endm
.macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
diff --git a/arch/arm/lib/memmove.S b/arch/arm/lib/memmove.S
index 12549187088..5025c863713 100644
--- a/arch/arm/lib/memmove.S
+++ b/arch/arm/lib/memmove.S
@@ -75,24 +75,24 @@ ENTRY(memmove)
addne pc, pc, ip @ C is always clear here
b 7f
6: nop
- ldr r3, [r1, #-4]!
- ldr r4, [r1, #-4]!
- ldr r5, [r1, #-4]!
- ldr r6, [r1, #-4]!
- ldr r7, [r1, #-4]!
- ldr r8, [r1, #-4]!
- ldr lr, [r1, #-4]!
+ W(ldr) r3, [r1, #-4]!
+ W(ldr) r4, [r1, #-4]!
+ W(ldr) r5, [r1, #-4]!
+ W(ldr) r6, [r1, #-4]!
+ W(ldr) r7, [r1, #-4]!
+ W(ldr) r8, [r1, #-4]!
+ W(ldr) lr, [r1, #-4]!
add pc, pc, ip
nop
nop
- str r3, [r0, #-4]!
- str r4, [r0, #-4]!
- str r5, [r0, #-4]!
- str r6, [r0, #-4]!
- str r7, [r0, #-4]!
- str r8, [r0, #-4]!
- str lr, [r0, #-4]!
+ W(str) r3, [r0, #-4]!
+ W(str) r4, [r0, #-4]!
+ W(str) r5, [r0, #-4]!
+ W(str) r6, [r0, #-4]!
+ W(str) r7, [r0, #-4]!
+ W(str) r8, [r0, #-4]!
+ W(str) lr, [r0, #-4]!
CALGN( bcs 2b )
diff --git a/arch/arm/lib/putuser.S b/arch/arm/lib/putuser.S
index 864f3c1c4f1..02fedbf07c0 100644
--- a/arch/arm/lib/putuser.S
+++ b/arch/arm/lib/putuser.S
@@ -37,6 +37,15 @@ ENDPROC(__put_user_1)
ENTRY(__put_user_2)
mov ip, r2, lsr #8
+#ifdef CONFIG_THUMB2_KERNEL
+#ifndef __ARMEB__
+2: strbt r2, [r0]
+3: strbt ip, [r0, #1]
+#else
+2: strbt ip, [r0]
+3: strbt r2, [r0, #1]
+#endif
+#else /* !CONFIG_THUMB2_KERNEL */
#ifndef __ARMEB__
2: strbt r2, [r0], #1
3: strbt ip, [r0]
@@ -44,6 +53,7 @@ ENTRY(__put_user_2)
2: strbt ip, [r0], #1
3: strbt r2, [r0]
#endif
+#endif /* CONFIG_THUMB2_KERNEL */
mov r0, #0
mov pc, lr
ENDPROC(__put_user_2)
@@ -55,8 +65,13 @@ ENTRY(__put_user_4)
ENDPROC(__put_user_4)
ENTRY(__put_user_8)
+#ifdef CONFIG_THUMB2_KERNEL
+5: strt r2, [r0]
+6: strt r3, [r0, #4]
+#else
5: strt r2, [r0], #4
6: strt r3, [r0]
+#endif
mov r0, #0
mov pc, lr
ENDPROC(__put_user_8)
diff --git a/arch/arm/lib/sha1.S b/arch/arm/lib/sha1.S
index a16fb208c84..09b548cac1a 100644
--- a/arch/arm/lib/sha1.S
+++ b/arch/arm/lib/sha1.S
@@ -187,6 +187,7 @@ ENTRY(sha_transform)
ENDPROC(sha_transform)
+ .align 2
.L_sha_K:
.word 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6
@@ -195,6 +196,7 @@ ENDPROC(sha_transform)
* void sha_init(__u32 *buf)
*/
+ .align 2
.L_sha_initial_digest:
.word 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0
diff --git a/arch/arm/lib/strncpy_from_user.S b/arch/arm/lib/strncpy_from_user.S
index 330373c26dd..1c9814f346c 100644
--- a/arch/arm/lib/strncpy_from_user.S
+++ b/arch/arm/lib/strncpy_from_user.S
@@ -23,7 +23,7 @@
ENTRY(__strncpy_from_user)
mov ip, r1
1: subs r2, r2, #1
-USER( ldrplbt r3, [r1], #1)
+ ldrusr r3, r1, 1, pl
bmi 2f
strb r3, [r0], #1
teq r3, #0
diff --git a/arch/arm/lib/strnlen_user.S b/arch/arm/lib/strnlen_user.S
index 90bb9d02083..7855b290665 100644
--- a/arch/arm/lib/strnlen_user.S
+++ b/arch/arm/lib/strnlen_user.S
@@ -23,7 +23,7 @@
ENTRY(__strnlen_user)
mov r2, r0
1:
-USER( ldrbt r3, [r0], #1)
+ ldrusr r3, r0, 1
teq r3, #0
beq 2f
subs r1, r1, #1
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 323b47f2b52..a24d824c428 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -23,6 +23,12 @@ config ARCH_AT91SAM9261
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
+config ARCH_AT91SAM9G10
+ bool "AT91SAM9G10"
+ select CPU_ARM926T
+ select GENERIC_TIME
+ select GENERIC_CLOCKEVENTS
+
config ARCH_AT91SAM9263
bool "AT91SAM9263"
select CPU_ARM926T
@@ -41,6 +47,12 @@ config ARCH_AT91SAM9G20
select GENERIC_TIME
select GENERIC_CLOCKEVENTS
+config ARCH_AT91SAM9G45
+ bool "AT91SAM9G45"
+ select CPU_ARM926T
+ select GENERIC_TIME
+ select GENERIC_CLOCKEVENTS
+
config ARCH_AT91CAP9
bool "AT91CAP9"
select CPU_ARM926T
@@ -144,6 +156,13 @@ config MACH_YL9200
help
Select this if you are using the ucDragon YL-9200 board.
+config MACH_CPUAT91
+ bool "Eukrea CPUAT91"
+ depends on ARCH_AT91RM9200
+ help
+ Select this if you are using the Eukrea Electromatique's
+ CPUAT91 board <http://www.eukrea.com/>.
+
endif
# ----------------------------------------------------------
@@ -205,6 +224,13 @@ config MACH_QIL_A9260
Select this if you are using a Calao Systems QIL-A9260 Board.
<http://www.calao-systems.com>
+config MACH_CPU9260
+ bool "Eukrea CPU9260 board"
+ depends on ARCH_AT91SAM9260
+ help
+ Select this if you are using a Eukrea Electromatique's
+ CPU9260 Board <http://www.eukrea.com/>
+
endif
# ----------------------------------------------------------
@@ -224,6 +250,21 @@ endif
# ----------------------------------------------------------
+if ARCH_AT91SAM9G10
+
+comment "AT91SAM9G10 Board Type"
+
+config MACH_AT91SAM9G10EK
+ bool "Atmel AT91SAM9G10-EK Evaluation Kit"
+ depends on ARCH_AT91SAM9G10
+ help
+ Select this if you are using Atmel's AT91SAM9G10-EK Evaluation Kit.
+ <http://www.atmel.com/dyn/products/tools_card.asp?tool_id=4588>
+
+endif
+
+# ----------------------------------------------------------
+
if ARCH_AT91SAM9263
comment "AT91SAM9263 Board Type"
@@ -276,6 +317,29 @@ config MACH_AT91SAM9G20EK
help
Select this if you are using Atmel's AT91SAM9G20-EK Evaluation Kit.
+config MACH_CPU9G20
+ bool "Eukrea CPU9G20 board"
+ depends on ARCH_AT91SAM9G20
+ help
+ Select this if you are using a Eukrea Electromatique's
+ CPU9G20 Board <http://www.eukrea.com/>
+
+endif
+
+# ----------------------------------------------------------
+
+if ARCH_AT91SAM9G45
+
+comment "AT91SAM9G45 Board Type"
+
+config MACH_AT91SAM9G45EKES
+ bool "Atmel AT91SAM9G45-EKES Evaluation Kit"
+ depends on ARCH_AT91SAM9G45
+ help
+ Select this if you are using Atmel's AT91SAM9G45-EKES Evaluation Kit.
+ "ES" at the end of the name means that this board is an
+ Engineering Sample.
+
endif
# ----------------------------------------------------------
@@ -315,13 +379,13 @@ comment "AT91 Board Options"
config MTD_AT91_DATAFLASH_CARD
bool "Enable DataFlash Card support"
- depends on (ARCH_AT91RM9200DK || MACH_AT91RM9200EK || MACH_AT91SAM9260EK || MACH_AT91SAM9261EK || MACH_AT91SAM9263EK || MACH_AT91SAM9G20EK || MACH_ECBAT91 || MACH_SAM9_L9260 || MACH_AT91CAP9ADK || MACH_NEOCORE926)
+ depends on (ARCH_AT91RM9200DK || MACH_AT91RM9200EK || MACH_AT91SAM9260EK || MACH_AT91SAM9261EK || MACH_AT91SAM9G10EK || MACH_AT91SAM9263EK || MACH_AT91SAM9G20EK || MACH_ECBAT91 || MACH_SAM9_L9260 || MACH_AT91CAP9ADK || MACH_NEOCORE926)
help
Enable support for the DataFlash card.
config MTD_NAND_ATMEL_BUSWIDTH_16
bool "Enable 16-bit data bus interface to NAND flash"
- depends on (MACH_AT91SAM9260EK || MACH_AT91SAM9261EK || MACH_AT91SAM9263EK || MACH_AT91SAM9G20EK || MACH_AT91CAP9ADK)
+ depends on (MACH_AT91SAM9260EK || MACH_AT91SAM9261EK || MACH_AT91SAM9G10EK || MACH_AT91SAM9263EK || MACH_AT91SAM9G20EK || MACH_AT91SAM9G45EKES || MACH_AT91CAP9ADK)
help
On AT91SAM926x boards both types of NAND flash can be present
(8 and 16 bit data bus width).
@@ -383,7 +447,7 @@ config AT91_EARLY_USART2
config AT91_EARLY_USART3
bool "USART3"
- depends on (ARCH_AT91RM9200 || ARCH_AT91SAM9RL || ARCH_AT91SAM9260 || ARCH_AT91SAM9G20)
+ depends on (ARCH_AT91RM9200 || ARCH_AT91SAM9RL || ARCH_AT91SAM9260 || ARCH_AT91SAM9G20 || ARCH_AT91SAM9G45)
config AT91_EARLY_USART4
bool "USART4"
diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile
index c69ff237fd1..a6ed015d82e 100644
--- a/arch/arm/mach-at91/Makefile
+++ b/arch/arm/mach-at91/Makefile
@@ -13,9 +13,11 @@ obj-$(CONFIG_AT91_PMC_UNIT) += clock.o
obj-$(CONFIG_ARCH_AT91RM9200) += at91rm9200.o at91rm9200_time.o at91rm9200_devices.o
obj-$(CONFIG_ARCH_AT91SAM9260) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o
obj-$(CONFIG_ARCH_AT91SAM9261) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o
+obj-$(CONFIG_ARCH_AT91SAM9G10) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o sam9_smc.o
obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o sam9_smc.o
obj-$(CONFIG_ARCH_AT91SAM9RL) += at91sam9rl.o at91sam926x_time.o at91sam9rl_devices.o sam9_smc.o
obj-$(CONFIG_ARCH_AT91SAM9G20) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o sam9_smc.o
+ obj-$(CONFIG_ARCH_AT91SAM9G45) += at91sam9g45.o at91sam926x_time.o at91sam9g45_devices.o sam9_smc.o
obj-$(CONFIG_ARCH_AT91CAP9) += at91cap9.o at91sam926x_time.o at91cap9_devices.o sam9_smc.o
obj-$(CONFIG_ARCH_AT91X40) += at91x40.o at91x40_time.o
@@ -32,6 +34,7 @@ obj-$(CONFIG_MACH_KAFA) += board-kafa.o
obj-$(CONFIG_MACH_PICOTUX2XX) += board-picotux200.o
obj-$(CONFIG_MACH_ECBAT91) += board-ecbat91.o
obj-$(CONFIG_MACH_YL9200) += board-yl-9200.o
+obj-$(CONFIG_MACH_CPUAT91) += board-cpuat91.o
# AT91SAM9260 board-specific support
obj-$(CONFIG_MACH_AT91SAM9260EK) += board-sam9260ek.o
@@ -40,9 +43,11 @@ obj-$(CONFIG_MACH_SAM9_L9260) += board-sam9-l9260.o
obj-$(CONFIG_MACH_USB_A9260) += board-usb-a9260.o
obj-$(CONFIG_MACH_QIL_A9260) += board-qil-a9260.o
obj-$(CONFIG_MACH_AFEB9260) += board-afeb-9260v1.o
+obj-$(CONFIG_MACH_CPU9260) += board-cpu9krea.o
# AT91SAM9261 board-specific support
obj-$(CONFIG_MACH_AT91SAM9261EK) += board-sam9261ek.o
+obj-$(CONFIG_MACH_AT91SAM9G10EK) += board-sam9261ek.o
# AT91SAM9263 board-specific support
obj-$(CONFIG_MACH_AT91SAM9263EK) += board-sam9263ek.o
@@ -54,6 +59,10 @@ obj-$(CONFIG_MACH_AT91SAM9RLEK) += board-sam9rlek.o
# AT91SAM9G20 board-specific support
obj-$(CONFIG_MACH_AT91SAM9G20EK) += board-sam9g20ek.o
+obj-$(CONFIG_MACH_CPU9G20) += board-cpu9krea.o
+
+# AT91SAM9G45 board-specific support
+obj-$(CONFIG_MACH_AT91SAM9G45EKES) += board-sam9m10g45ek.o
# AT91CAP9 board-specific support
obj-$(CONFIG_MACH_AT91CAP9ADK) += board-cap9adk.o
diff --git a/arch/arm/mach-at91/Makefile.boot b/arch/arm/mach-at91/Makefile.boot
index 071a2506a69..3462b815054 100644
--- a/arch/arm/mach-at91/Makefile.boot
+++ b/arch/arm/mach-at91/Makefile.boot
@@ -7,6 +7,10 @@ ifeq ($(CONFIG_ARCH_AT91CAP9),y)
zreladdr-y := 0x70008000
params_phys-y := 0x70000100
initrd_phys-y := 0x70410000
+else ifeq ($(CONFIG_ARCH_AT91SAM9G45),y)
+ zreladdr-y := 0x70008000
+params_phys-y := 0x70000100
+initrd_phys-y := 0x70410000
else
zreladdr-y := 0x20008000
params_phys-y := 0x20000100
diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index d74c9ac007e..ee4ea0e720c 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -1113,6 +1113,122 @@ void __init at91_set_serial_console(unsigned portnr) {}
void __init at91_add_device_serial(void) {}
#endif
+/* --------------------------------------------------------------------
+ * CF/IDE
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_BLK_DEV_IDE_AT91) || defined(CONFIG_BLK_DEV_IDE_AT91_MODULE) || \
+ defined(CONFIG_PATA_AT91) || defined(CONFIG_PATA_AT91_MODULE) || \
+ defined(CONFIG_AT91_CF) || defined(CONFIG_AT91_CF_MODULE)
+
+static struct at91_cf_data cf0_data;
+
+static struct resource cf0_resources[] = {
+ [0] = {
+ .start = AT91_CHIPSELECT_4,
+ .end = AT91_CHIPSELECT_4 + SZ_256M - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device cf0_device = {
+ .id = 0,
+ .dev = {
+ .platform_data = &cf0_data,
+ },
+ .resource = cf0_resources,
+ .num_resources = ARRAY_SIZE(cf0_resources),
+};
+
+static struct at91_cf_data cf1_data;
+
+static struct resource cf1_resources[] = {
+ [0] = {
+ .start = AT91_CHIPSELECT_5,
+ .end = AT91_CHIPSELECT_5 + SZ_256M - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device cf1_device = {
+ .id = 1,
+ .dev = {
+ .platform_data = &cf1_data,
+ },
+ .resource = cf1_resources,
+ .num_resources = ARRAY_SIZE(cf1_resources),
+};
+
+void __init at91_add_device_cf(struct at91_cf_data *data)
+{
+ struct platform_device *pdev;
+ unsigned long csa;
+
+ if (!data)
+ return;
+
+ csa = at91_sys_read(AT91_MATRIX_EBICSA);
+
+ switch (data->chipselect) {
+ case 4:
+ at91_set_multi_drive(AT91_PIN_PC8, 0);
+ at91_set_A_periph(AT91_PIN_PC8, 0);
+ csa |= AT91_MATRIX_CS4A_SMC_CF1;
+ cf0_data = *data;
+ pdev = &cf0_device;
+ break;
+ case 5:
+ at91_set_multi_drive(AT91_PIN_PC9, 0);
+ at91_set_A_periph(AT91_PIN_PC9, 0);
+ csa |= AT91_MATRIX_CS5A_SMC_CF2;
+ cf1_data = *data;
+ pdev = &cf1_device;
+ break;
+ default:
+ printk(KERN_ERR "AT91 CF: bad chip-select requested (%u)\n",
+ data->chipselect);
+ return;
+ }
+
+ at91_sys_write(AT91_MATRIX_EBICSA, csa);
+
+ if (data->rst_pin) {
+ at91_set_multi_drive(data->rst_pin, 0);
+ at91_set_gpio_output(data->rst_pin, 1);
+ }
+
+ if (data->irq_pin) {
+ at91_set_gpio_input(data->irq_pin, 0);
+ at91_set_deglitch(data->irq_pin, 1);
+ }
+
+ if (data->det_pin) {
+ at91_set_gpio_input(data->det_pin, 0);
+ at91_set_deglitch(data->det_pin, 1);
+ }
+
+ at91_set_B_periph(AT91_PIN_PC6, 0); /* CFCE1 */
+ at91_set_B_periph(AT91_PIN_PC7, 0); /* CFCE2 */
+ at91_set_A_periph(AT91_PIN_PC10, 0); /* CFRNW */
+ at91_set_A_periph(AT91_PIN_PC15, 1); /* NWAIT */
+
+ if (data->flags & AT91_CF_TRUE_IDE)
+#if defined(CONFIG_PATA_AT91) || defined(CONFIG_PATA_AT91_MODULE)
+ pdev->name = "pata_at91";
+#elif defined(CONFIG_BLK_DEV_IDE_AT91) || defined(CONFIG_BLK_DEV_IDE_AT91_MODULE)
+ pdev->name = "at91_ide";
+#else
+#warning "board requires AT91_CF_TRUE_IDE: enable either at91_ide or pata_at91"
+#endif
+ else
+ pdev->name = "at91_cf";
+
+ platform_device_register(pdev);
+}
+
+#else
+void __init at91_add_device_cf(struct at91_cf_data * data) {}
+#endif
/* -------------------------------------------------------------------- */
/*
diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
index 3acd7d7e6a4..4ecf37996c7 100644
--- a/arch/arm/mach-at91/at91sam9261.c
+++ b/arch/arm/mach-at91/at91sam9261.c
@@ -16,6 +16,7 @@
#include <asm/irq.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
+#include <mach/cpu.h>
#include <mach/at91sam9261.h>
#include <mach/at91_pmc.h>
#include <mach/at91_rstc.h>
@@ -30,7 +31,11 @@ static struct map_desc at91sam9261_io_desc[] __initdata = {
.pfn = __phys_to_pfn(AT91_BASE_SYS),
.length = SZ_16K,
.type = MT_DEVICE,
- }, {
+ },
+};
+
+static struct map_desc at91sam9261_sram_desc[] __initdata = {
+ {
.virtual = AT91_IO_VIRT_BASE - AT91SAM9261_SRAM_SIZE,
.pfn = __phys_to_pfn(AT91SAM9261_SRAM_BASE),
.length = AT91SAM9261_SRAM_SIZE,
@@ -38,6 +43,15 @@ static struct map_desc at91sam9261_io_desc[] __initdata = {
},
};
+static struct map_desc at91sam9g10_sram_desc[] __initdata = {
+ {
+ .virtual = AT91_IO_VIRT_BASE - AT91SAM9G10_SRAM_SIZE,
+ .pfn = __phys_to_pfn(AT91SAM9G10_SRAM_BASE),
+ .length = AT91SAM9G10_SRAM_SIZE,
+ .type = MT_DEVICE,
+ },
+};
+
/* --------------------------------------------------------------------
* Clocks
* -------------------------------------------------------------------- */
@@ -263,6 +277,12 @@ void __init at91sam9261_initialize(unsigned long main_clock)
/* Map peripherals */
iotable_init(at91sam9261_io_desc, ARRAY_SIZE(at91sam9261_io_desc));
+ if (cpu_is_at91sam9g10())
+ iotable_init(at91sam9g10_sram_desc, ARRAY_SIZE(at91sam9g10_sram_desc));
+ else
+ iotable_init(at91sam9261_sram_desc, ARRAY_SIZE(at91sam9261_sram_desc));
+
+
at91_arch_reset = at91sam9261_reset;
pm_power_off = at91sam9261_poweroff;
at91_extern_irq = (1 << AT91SAM9261_ID_IRQ0) | (1 << AT91SAM9261_ID_IRQ1)
diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c
index b7f23324231..55719a97427 100644
--- a/arch/arm/mach-at91/at91sam9263_devices.c
+++ b/arch/arm/mach-at91/at91sam9263_devices.c
@@ -707,9 +707,9 @@ void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices)
* AC97
* -------------------------------------------------------------------- */
-#if defined(CONFIG_SND_AT91_AC97) || defined(CONFIG_SND_AT91_AC97_MODULE)
+#if defined(CONFIG_SND_ATMEL_AC97C) || defined(CONFIG_SND_ATMEL_AC97C_MODULE)
static u64 ac97_dmamask = DMA_BIT_MASK(32);
-static struct atmel_ac97_data ac97_data;
+static struct ac97c_platform_data ac97_data;
static struct resource ac97_resources[] = {
[0] = {
@@ -725,8 +725,8 @@ static struct resource ac97_resources[] = {
};
static struct platform_device at91sam9263_ac97_device = {
- .name = "ac97c",
- .id = 1,
+ .name = "atmel_ac97c",
+ .id = 0,
.dev = {
.dma_mask = &ac97_dmamask,
.coherent_dma_mask = DMA_BIT_MASK(32),
@@ -736,7 +736,7 @@ static struct platform_device at91sam9263_ac97_device = {
.num_resources = ARRAY_SIZE(ac97_resources),
};
-void __init at91_add_device_ac97(struct atmel_ac97_data *data)
+void __init at91_add_device_ac97(struct ac97c_platform_data *data)
{
if (!data)
return;
@@ -750,11 +750,11 @@ void __init at91_add_device_ac97(struct atmel_ac97_data *data)
if (data->reset_pin)
at91_set_gpio_output(data->reset_pin, 0);
- ac97_data = *ek_data;
+ ac97_data = *data;
platform_device_register(&at91sam9263_ac97_device);
}
#else
-void __init at91_add_device_ac97(struct atmel_ac97_data *data) {}
+void __init at91_add_device_ac97(struct ac97c_platform_data *data) {}
#endif
diff --git a/arch/arm/mach-at91/at91sam9g45.c b/arch/arm/mach-at91/at91sam9g45.c
new file mode 100644
index 00000000000..85166b7e69a
--- /dev/null
+++ b/arch/arm/mach-at91/at91sam9g45.c
@@ -0,0 +1,360 @@
+/*
+ * Chip-specific setup code for the AT91SAM9G45 family
+ *
+ * Copyright (C) 2009 Atmel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pm.h>
+
+#include <asm/irq.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <mach/at91sam9g45.h>
+#include <mach/at91_pmc.h>
+#include <mach/at91_rstc.h>
+#include <mach/at91_shdwc.h>
+
+#include "generic.h"
+#include "clock.h"
+
+static struct map_desc at91sam9g45_io_desc[] __initdata = {
+ {
+ .virtual = AT91_VA_BASE_SYS,
+ .pfn = __phys_to_pfn(AT91_BASE_SYS),
+ .length = SZ_16K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = AT91_IO_VIRT_BASE - AT91SAM9G45_SRAM_SIZE,
+ .pfn = __phys_to_pfn(AT91SAM9G45_SRAM_BASE),
+ .length = AT91SAM9G45_SRAM_SIZE,
+ .type = MT_DEVICE,
+ }
+};
+
+/* --------------------------------------------------------------------
+ * Clocks
+ * -------------------------------------------------------------------- */
+
+/*
+ * The peripheral clocks.
+ */
+static struct clk pioA_clk = {
+ .name = "pioA_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_PIOA,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk pioB_clk = {
+ .name = "pioB_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_PIOB,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk pioC_clk = {
+ .name = "pioC_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_PIOC,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk pioDE_clk = {
+ .name = "pioDE_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_PIODE,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk usart0_clk = {
+ .name = "usart0_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_US0,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk usart1_clk = {
+ .name = "usart1_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_US1,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk usart2_clk = {
+ .name = "usart2_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_US2,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk usart3_clk = {
+ .name = "usart3_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_US3,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk mmc0_clk = {
+ .name = "mci0_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_MCI0,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk twi0_clk = {
+ .name = "twi0_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_TWI0,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk twi1_clk = {
+ .name = "twi1_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_TWI1,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk spi0_clk = {
+ .name = "spi0_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_SPI0,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk spi1_clk = {
+ .name = "spi1_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_SPI1,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk ssc0_clk = {
+ .name = "ssc0_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_SSC0,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk ssc1_clk = {
+ .name = "ssc1_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_SSC1,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk tcb_clk = {
+ .name = "tcb_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_TCB,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk pwm_clk = {
+ .name = "pwm_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_PWMC,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk tsc_clk = {
+ .name = "tsc_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_TSC,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk dma_clk = {
+ .name = "dma_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_DMA,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk uhphs_clk = {
+ .name = "uhphs_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_UHPHS,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk lcdc_clk = {
+ .name = "lcdc_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_LCDC,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk ac97_clk = {
+ .name = "ac97_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_AC97C,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk macb_clk = {
+ .name = "macb_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_EMAC,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk isi_clk = {
+ .name = "isi_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_ISI,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk udphs_clk = {
+ .name = "udphs_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_UDPHS,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+static struct clk mmc1_clk = {
+ .name = "mci1_clk",
+ .pmc_mask = 1 << AT91SAM9G45_ID_MCI1,
+ .type = CLK_TYPE_PERIPHERAL,
+};
+
+/* One additional fake clock for ohci */
+static struct clk ohci_clk = {
+ .name = "ohci_clk",
+ .pmc_mask = 0,
+ .type = CLK_TYPE_PERIPHERAL,
+ .parent = &uhphs_clk,
+};
+
+static struct clk *periph_clocks[] __initdata = {
+ &pioA_clk,
+ &pioB_clk,
+ &pioC_clk,
+ &pioDE_clk,
+ &usart0_clk,
+ &usart1_clk,
+ &usart2_clk,
+ &usart3_clk,
+ &mmc0_clk,
+ &twi0_clk,
+ &twi1_clk,
+ &spi0_clk,
+ &spi1_clk,
+ &ssc0_clk,
+ &ssc1_clk,
+ &tcb_clk,
+ &pwm_clk,
+ &tsc_clk,
+ &dma_clk,
+ &uhphs_clk,
+ &lcdc_clk,
+ &ac97_clk,
+ &macb_clk,
+ &isi_clk,
+ &udphs_clk,
+ &mmc1_clk,
+ // irq0
+ &ohci_clk,
+};
+
+/*
+ * The two programmable clocks.
+ * You must configure pin multiplexing to bring these signals out.
+ */
+static struct clk pck0 = {
+ .name = "pck0",
+ .pmc_mask = AT91_PMC_PCK0,
+ .type = CLK_TYPE_PROGRAMMABLE,
+ .id = 0,
+};
+static struct clk pck1 = {
+ .name = "pck1",
+ .pmc_mask = AT91_PMC_PCK1,
+ .type = CLK_TYPE_PROGRAMMABLE,
+ .id = 1,
+};
+
+static void __init at91sam9g45_register_clocks(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(periph_clocks); i++)
+ clk_register(periph_clocks[i]);
+
+ clk_register(&pck0);
+ clk_register(&pck1);
+}
+
+/* --------------------------------------------------------------------
+ * GPIO
+ * -------------------------------------------------------------------- */
+
+static struct at91_gpio_bank at91sam9g45_gpio[] = {
+ {
+ .id = AT91SAM9G45_ID_PIOA,
+ .offset = AT91_PIOA,
+ .clock = &pioA_clk,
+ }, {
+ .id = AT91SAM9G45_ID_PIOB,
+ .offset = AT91_PIOB,
+ .clock = &pioB_clk,
+ }, {
+ .id = AT91SAM9G45_ID_PIOC,
+ .offset = AT91_PIOC,
+ .clock = &pioC_clk,
+ }, {
+ .id = AT91SAM9G45_ID_PIODE,
+ .offset = AT91_PIOD,
+ .clock = &pioDE_clk,
+ }, {
+ .id = AT91SAM9G45_ID_PIODE,
+ .offset = AT91_PIOE,
+ .clock = &pioDE_clk,
+ }
+};
+
+static void at91sam9g45_reset(void)
+{
+ at91_sys_write(AT91_RSTC_CR, AT91_RSTC_KEY | AT91_RSTC_PROCRST | AT91_RSTC_PERRST);
+}
+
+static void at91sam9g45_poweroff(void)
+{
+ at91_sys_write(AT91_SHDW_CR, AT91_SHDW_KEY | AT91_SHDW_SHDW);
+}
+
+
+/* --------------------------------------------------------------------
+ * AT91SAM9G45 processor initialization
+ * -------------------------------------------------------------------- */
+
+void __init at91sam9g45_initialize(unsigned long main_clock)
+{
+ /* Map peripherals */
+ iotable_init(at91sam9g45_io_desc, ARRAY_SIZE(at91sam9g45_io_desc));
+
+ at91_arch_reset = at91sam9g45_reset;
+ pm_power_off = at91sam9g45_poweroff;
+ at91_extern_irq = (1 << AT91SAM9G45_ID_IRQ0);
+
+ /* Init clock subsystem */
+ at91_clock_init(main_clock);
+
+ /* Register the processor-specific clocks */
+ at91sam9g45_register_clocks();
+
+ /* Register GPIO subsystem */
+ at91_gpio_init(at91sam9g45_gpio, 5);
+}
+
+/* --------------------------------------------------------------------
+ * Interrupt initialization
+ * -------------------------------------------------------------------- */
+
+/*
+ * The default interrupt priority levels (0 = lowest, 7 = highest).
+ */
+static unsigned int at91sam9g45_default_irq_priority[NR_AIC_IRQS] __initdata = {
+ 7, /* Advanced Interrupt Controller (FIQ) */
+ 7, /* System Peripherals */
+ 1, /* Parallel IO Controller A */
+ 1, /* Parallel IO Controller B */
+ 1, /* Parallel IO Controller C */
+ 1, /* Parallel IO Controller D and E */
+ 0,
+ 5, /* USART 0 */
+ 5, /* USART 1 */
+ 5, /* USART 2 */
+ 5, /* USART 3 */
+ 0, /* Multimedia Card Interface 0 */
+ 6, /* Two-Wire Interface 0 */
+ 6, /* Two-Wire Interface 1 */
+ 5, /* Serial Peripheral Interface 0 */
+ 5, /* Serial Peripheral Interface 1 */
+ 4, /* Serial Synchronous Controller 0 */
+ 4, /* Serial Synchronous Controller 1 */
+ 0, /* Timer Counter 0, 1, 2, 3, 4 and 5 */
+ 0, /* Pulse Width Modulation Controller */
+ 0, /* Touch Screen Controller */
+ 0, /* DMA Controller */
+ 2, /* USB Host High Speed port */
+ 3, /* LDC Controller */
+ 5, /* AC97 Controller */
+ 3, /* Ethernet */
+ 0, /* Image Sensor Interface */
+ 2, /* USB Device High speed port */
+ 0,
+ 0, /* Multimedia Card Interface 1 */
+ 0,
+ 0, /* Advanced Interrupt Controller (IRQ0) */
+};
+
+void __init at91sam9g45_init_interrupts(unsigned int priority[NR_AIC_IRQS])
+{
+ if (!priority)
+ priority = at91sam9g45_default_irq_priority;
+
+ /* Initialize the AIC interrupt controller */
+ at91_aic_init(priority);
+
+ /* Enable GPIO interrupts */
+ at91_gpio_irq_setup();
+}
diff --git a/arch/arm/mach-at91/at91sam9g45_devices.c b/arch/arm/mach-at91/at91sam9g45_devices.c
new file mode 100644
index 00000000000..d746e8621bc
--- /dev/null
+++ b/arch/arm/mach-at91/at91sam9g45_devices.c
@@ -0,0 +1,1230 @@
+/*
+ * On-Chip devices setup code for the AT91SAM9G45 family
+ *
+ * Copyright (C) 2009 Atmel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/i2c-gpio.h>
+
+#include <linux/fb.h>
+#include <video/atmel_lcdc.h>
+
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/at91sam9g45.h>
+#include <mach/at91sam9g45_matrix.h>
+#include <mach/at91sam9_smc.h>
+
+#include "generic.h"
+
+
+/* --------------------------------------------------------------------
+ * USB Host (OHCI)
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE)
+static u64 ohci_dmamask = DMA_BIT_MASK(32);
+static struct at91_usbh_data usbh_ohci_data;
+
+static struct resource usbh_ohci_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_OHCI_BASE,
+ .end = AT91SAM9G45_OHCI_BASE + SZ_1M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_UHPHS,
+ .end = AT91SAM9G45_ID_UHPHS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91_usbh_ohci_device = {
+ .name = "at91_ohci",
+ .id = -1,
+ .dev = {
+ .dma_mask = &ohci_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &usbh_ohci_data,
+ },
+ .resource = usbh_ohci_resources,
+ .num_resources = ARRAY_SIZE(usbh_ohci_resources),
+};
+
+void __init at91_add_device_usbh_ohci(struct at91_usbh_data *data)
+{
+ int i;
+
+ if (!data)
+ return;
+
+ /* Enable VBus control for UHP ports */
+ for (i = 0; i < data->ports; i++) {
+ if (data->vbus_pin[i])
+ at91_set_gpio_output(data->vbus_pin[i], 0);
+ }
+
+ usbh_ohci_data = *data;
+ platform_device_register(&at91_usbh_ohci_device);
+}
+#else
+void __init at91_add_device_usbh_ohci(struct at91_usbh_data *data) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * USB HS Device (Gadget)
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_USB_GADGET_ATMEL_USBA) || defined(CONFIG_USB_GADGET_ATMEL_USBA_MODULE)
+static struct resource usba_udc_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_UDPHS_FIFO,
+ .end = AT91SAM9G45_UDPHS_FIFO + SZ_512K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_BASE_UDPHS,
+ .end = AT91SAM9G45_BASE_UDPHS + SZ_1K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .start = AT91SAM9G45_ID_UDPHS,
+ .end = AT91SAM9G45_ID_UDPHS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+#define EP(nam, idx, maxpkt, maxbk, dma, isoc) \
+ [idx] = { \
+ .name = nam, \
+ .index = idx, \
+ .fifo_size = maxpkt, \
+ .nr_banks = maxbk, \
+ .can_dma = dma, \
+ .can_isoc = isoc, \
+ }
+
+static struct usba_ep_data usba_udc_ep[] __initdata = {
+ EP("ep0", 0, 64, 1, 0, 0),
+ EP("ep1", 1, 1024, 2, 1, 1),
+ EP("ep2", 2, 1024, 2, 1, 1),
+ EP("ep3", 3, 1024, 3, 1, 0),
+ EP("ep4", 4, 1024, 3, 1, 0),
+ EP("ep5", 5, 1024, 3, 1, 1),
+ EP("ep6", 6, 1024, 3, 1, 1),
+};
+
+#undef EP
+
+/*
+ * pdata doesn't have room for any endpoints, so we need to
+ * append room for the ones we need right after it.
+ */
+static struct {
+ struct usba_platform_data pdata;
+ struct usba_ep_data ep[7];
+} usba_udc_data;
+
+static struct platform_device at91_usba_udc_device = {
+ .name = "atmel_usba_udc",
+ .id = -1,
+ .dev = {
+ .platform_data = &usba_udc_data.pdata,
+ },
+ .resource = usba_udc_resources,
+ .num_resources = ARRAY_SIZE(usba_udc_resources),
+};
+
+void __init at91_add_device_usba(struct usba_platform_data *data)
+{
+ usba_udc_data.pdata.vbus_pin = -EINVAL;
+ usba_udc_data.pdata.num_ep = ARRAY_SIZE(usba_udc_ep);
+ memcpy(usba_udc_data.ep, usba_udc_ep, sizeof(usba_udc_ep));;
+
+ if (data && data->vbus_pin > 0) {
+ at91_set_gpio_input(data->vbus_pin, 0);
+ at91_set_deglitch(data->vbus_pin, 1);
+ usba_udc_data.pdata.vbus_pin = data->vbus_pin;
+ }
+
+ /* Pullup pin is handled internally by USB device peripheral */
+
+ /* Clocks */
+ at91_clock_associate("utmi_clk", &at91_usba_udc_device.dev, "hclk");
+ at91_clock_associate("udphs_clk", &at91_usba_udc_device.dev, "pclk");
+
+ platform_device_register(&at91_usba_udc_device);
+}
+#else
+void __init at91_add_device_usba(struct usba_platform_data *data) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * Ethernet
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_MACB) || defined(CONFIG_MACB_MODULE)
+static u64 eth_dmamask = DMA_BIT_MASK(32);
+static struct at91_eth_data eth_data;
+
+static struct resource eth_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_EMAC,
+ .end = AT91SAM9G45_BASE_EMAC + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_EMAC,
+ .end = AT91SAM9G45_ID_EMAC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91sam9g45_eth_device = {
+ .name = "macb",
+ .id = -1,
+ .dev = {
+ .dma_mask = &eth_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &eth_data,
+ },
+ .resource = eth_resources,
+ .num_resources = ARRAY_SIZE(eth_resources),
+};
+
+void __init at91_add_device_eth(struct at91_eth_data *data)
+{
+ if (!data)
+ return;
+
+ if (data->phy_irq_pin) {
+ at91_set_gpio_input(data->phy_irq_pin, 0);
+ at91_set_deglitch(data->phy_irq_pin, 1);
+ }
+
+ /* Pins used for MII and RMII */
+ at91_set_A_periph(AT91_PIN_PA17, 0); /* ETXCK_EREFCK */
+ at91_set_A_periph(AT91_PIN_PA15, 0); /* ERXDV */
+ at91_set_A_periph(AT91_PIN_PA12, 0); /* ERX0 */
+ at91_set_A_periph(AT91_PIN_PA13, 0); /* ERX1 */
+ at91_set_A_periph(AT91_PIN_PA16, 0); /* ERXER */
+ at91_set_A_periph(AT91_PIN_PA14, 0); /* ETXEN */
+ at91_set_A_periph(AT91_PIN_PA10, 0); /* ETX0 */
+ at91_set_A_periph(AT91_PIN_PA11, 0); /* ETX1 */
+ at91_set_A_periph(AT91_PIN_PA19, 0); /* EMDIO */
+ at91_set_A_periph(AT91_PIN_PA18, 0); /* EMDC */
+
+ if (!data->is_rmii) {
+ at91_set_B_periph(AT91_PIN_PA29, 0); /* ECRS */
+ at91_set_B_periph(AT91_PIN_PA30, 0); /* ECOL */
+ at91_set_B_periph(AT91_PIN_PA8, 0); /* ERX2 */
+ at91_set_B_periph(AT91_PIN_PA9, 0); /* ERX3 */
+ at91_set_B_periph(AT91_PIN_PA28, 0); /* ERXCK */
+ at91_set_B_periph(AT91_PIN_PA6, 0); /* ETX2 */
+ at91_set_B_periph(AT91_PIN_PA7, 0); /* ETX3 */
+ at91_set_B_periph(AT91_PIN_PA27, 0); /* ETXER */
+ }
+
+ eth_data = *data;
+ platform_device_register(&at91sam9g45_eth_device);
+}
+#else
+void __init at91_add_device_eth(struct at91_eth_data *data) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * NAND / SmartMedia
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_MTD_NAND_ATMEL) || defined(CONFIG_MTD_NAND_ATMEL_MODULE)
+static struct atmel_nand_data nand_data;
+
+#define NAND_BASE AT91_CHIPSELECT_3
+
+static struct resource nand_resources[] = {
+ [0] = {
+ .start = NAND_BASE,
+ .end = NAND_BASE + SZ_256M - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91_BASE_SYS + AT91_ECC,
+ .end = AT91_BASE_SYS + AT91_ECC + SZ_512 - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device at91sam9g45_nand_device = {
+ .name = "atmel_nand",
+ .id = -1,
+ .dev = {
+ .platform_data = &nand_data,
+ },
+ .resource = nand_resources,
+ .num_resources = ARRAY_SIZE(nand_resources),
+};
+
+void __init at91_add_device_nand(struct atmel_nand_data *data)
+{
+ unsigned long csa;
+
+ if (!data)
+ return;
+
+ csa = at91_sys_read(AT91_MATRIX_EBICSA);
+ at91_sys_write(AT91_MATRIX_EBICSA, csa | AT91_MATRIX_EBI_CS3A_SMC_SMARTMEDIA);
+
+ /* enable pin */
+ if (data->enable_pin)
+ at91_set_gpio_output(data->enable_pin, 1);
+
+ /* ready/busy pin */
+ if (data->rdy_pin)
+ at91_set_gpio_input(data->rdy_pin, 1);
+
+ /* card detect pin */
+ if (data->det_pin)
+ at91_set_gpio_input(data->det_pin, 1);
+
+ nand_data = *data;
+ platform_device_register(&at91sam9g45_nand_device);
+}
+#else
+void __init at91_add_device_nand(struct atmel_nand_data *data) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * TWI (i2c)
+ * -------------------------------------------------------------------- */
+
+/*
+ * Prefer the GPIO code since the TWI controller isn't robust
+ * (gets overruns and underruns under load) and can only issue
+ * repeated STARTs in one scenario (the driver doesn't yet handle them).
+ */
+#if defined(CONFIG_I2C_GPIO) || defined(CONFIG_I2C_GPIO_MODULE)
+static struct i2c_gpio_platform_data pdata_i2c0 = {
+ .sda_pin = AT91_PIN_PA20,
+ .sda_is_open_drain = 1,
+ .scl_pin = AT91_PIN_PA21,
+ .scl_is_open_drain = 1,
+ .udelay = 2, /* ~100 kHz */
+};
+
+static struct platform_device at91sam9g45_twi0_device = {
+ .name = "i2c-gpio",
+ .id = 0,
+ .dev.platform_data = &pdata_i2c0,
+};
+
+static struct i2c_gpio_platform_data pdata_i2c1 = {
+ .sda_pin = AT91_PIN_PB10,
+ .sda_is_open_drain = 1,
+ .scl_pin = AT91_PIN_PB11,
+ .scl_is_open_drain = 1,
+ .udelay = 2, /* ~100 kHz */
+};
+
+static struct platform_device at91sam9g45_twi1_device = {
+ .name = "i2c-gpio",
+ .id = 1,
+ .dev.platform_data = &pdata_i2c1,
+};
+
+void __init at91_add_device_i2c(short i2c_id, struct i2c_board_info *devices, int nr_devices)
+{
+ i2c_register_board_info(i2c_id, devices, nr_devices);
+
+ if (i2c_id == 0) {
+ at91_set_GPIO_periph(AT91_PIN_PA20, 1); /* TWD (SDA) */
+ at91_set_multi_drive(AT91_PIN_PA20, 1);
+
+ at91_set_GPIO_periph(AT91_PIN_PA21, 1); /* TWCK (SCL) */
+ at91_set_multi_drive(AT91_PIN_PA21, 1);
+
+ platform_device_register(&at91sam9g45_twi0_device);
+ } else {
+ at91_set_GPIO_periph(AT91_PIN_PB10, 1); /* TWD (SDA) */
+ at91_set_multi_drive(AT91_PIN_PB10, 1);
+
+ at91_set_GPIO_periph(AT91_PIN_PB11, 1); /* TWCK (SCL) */
+ at91_set_multi_drive(AT91_PIN_PB11, 1);
+
+ platform_device_register(&at91sam9g45_twi1_device);
+ }
+}
+
+#elif defined(CONFIG_I2C_AT91) || defined(CONFIG_I2C_AT91_MODULE)
+static struct resource twi0_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_TWI0,
+ .end = AT91SAM9G45_BASE_TWI0 + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_TWI0,
+ .end = AT91SAM9G45_ID_TWI0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91sam9g45_twi0_device = {
+ .name = "at91_i2c",
+ .id = 0,
+ .resource = twi0_resources,
+ .num_resources = ARRAY_SIZE(twi0_resources),
+};
+
+static struct resource twi1_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_TWI1,
+ .end = AT91SAM9G45_BASE_TWI1 + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_TWI1,
+ .end = AT91SAM9G45_ID_TWI1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91sam9g45_twi1_device = {
+ .name = "at91_i2c",
+ .id = 1,
+ .resource = twi1_resources,
+ .num_resources = ARRAY_SIZE(twi1_resources),
+};
+
+void __init at91_add_device_i2c(short i2c_id, struct i2c_board_info *devices, int nr_devices)
+{
+ i2c_register_board_info(i2c_id, devices, nr_devices);
+
+ /* pins used for TWI interface */
+ if (i2c_id == 0) {
+ at91_set_A_periph(AT91_PIN_PA20, 0); /* TWD */
+ at91_set_multi_drive(AT91_PIN_PA20, 1);
+
+ at91_set_A_periph(AT91_PIN_PA21, 0); /* TWCK */
+ at91_set_multi_drive(AT91_PIN_PA21, 1);
+
+ platform_device_register(&at91sam9g45_twi0_device);
+ } else {
+ at91_set_A_periph(AT91_PIN_PB10, 0); /* TWD */
+ at91_set_multi_drive(AT91_PIN_PB10, 1);
+
+ at91_set_A_periph(AT91_PIN_PB11, 0); /* TWCK */
+ at91_set_multi_drive(AT91_PIN_PB11, 1);
+
+ platform_device_register(&at91sam9g45_twi1_device);
+ }
+}
+#else
+void __init at91_add_device_i2c(short i2c_id, struct i2c_board_info *devices, int nr_devices) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * SPI
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_SPI_ATMEL) || defined(CONFIG_SPI_ATMEL_MODULE)
+static u64 spi_dmamask = DMA_BIT_MASK(32);
+
+static struct resource spi0_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_SPI0,
+ .end = AT91SAM9G45_BASE_SPI0 + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_SPI0,
+ .end = AT91SAM9G45_ID_SPI0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91sam9g45_spi0_device = {
+ .name = "atmel_spi",
+ .id = 0,
+ .dev = {
+ .dma_mask = &spi_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .resource = spi0_resources,
+ .num_resources = ARRAY_SIZE(spi0_resources),
+};
+
+static const unsigned spi0_standard_cs[4] = { AT91_PIN_PB3, AT91_PIN_PB18, AT91_PIN_PB19, AT91_PIN_PD27 };
+
+static struct resource spi1_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_SPI1,
+ .end = AT91SAM9G45_BASE_SPI1 + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_SPI1,
+ .end = AT91SAM9G45_ID_SPI1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91sam9g45_spi1_device = {
+ .name = "atmel_spi",
+ .id = 1,
+ .dev = {
+ .dma_mask = &spi_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .resource = spi1_resources,
+ .num_resources = ARRAY_SIZE(spi1_resources),
+};
+
+static const unsigned spi1_standard_cs[4] = { AT91_PIN_PB17, AT91_PIN_PD28, AT91_PIN_PD18, AT91_PIN_PD19 };
+
+void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices)
+{
+ int i;
+ unsigned long cs_pin;
+ short enable_spi0 = 0;
+ short enable_spi1 = 0;
+
+ /* Choose SPI chip-selects */
+ for (i = 0; i < nr_devices; i++) {
+ if (devices[i].controller_data)
+ cs_pin = (unsigned long) devices[i].controller_data;
+ else if (devices[i].bus_num == 0)
+ cs_pin = spi0_standard_cs[devices[i].chip_select];
+ else
+ cs_pin = spi1_standard_cs[devices[i].chip_select];
+
+ if (devices[i].bus_num == 0)
+ enable_spi0 = 1;
+ else
+ enable_spi1 = 1;
+
+ /* enable chip-select pin */
+ at91_set_gpio_output(cs_pin, 1);
+
+ /* pass chip-select pin to driver */
+ devices[i].controller_data = (void *) cs_pin;
+ }
+
+ spi_register_board_info(devices, nr_devices);
+
+ /* Configure SPI bus(es) */
+ if (enable_spi0) {
+ at91_set_A_periph(AT91_PIN_PB0, 0); /* SPI0_MISO */
+ at91_set_A_periph(AT91_PIN_PB1, 0); /* SPI0_MOSI */
+ at91_set_A_periph(AT91_PIN_PB2, 0); /* SPI0_SPCK */
+
+ at91_clock_associate("spi0_clk", &at91sam9g45_spi0_device.dev, "spi_clk");
+ platform_device_register(&at91sam9g45_spi0_device);
+ }
+ if (enable_spi1) {
+ at91_set_A_periph(AT91_PIN_PB14, 0); /* SPI1_MISO */
+ at91_set_A_periph(AT91_PIN_PB15, 0); /* SPI1_MOSI */
+ at91_set_A_periph(AT91_PIN_PB16, 0); /* SPI1_SPCK */
+
+ at91_clock_associate("spi1_clk", &at91sam9g45_spi1_device.dev, "spi_clk");
+ platform_device_register(&at91sam9g45_spi1_device);
+ }
+}
+#else
+void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * LCD Controller
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_FB_ATMEL) || defined(CONFIG_FB_ATMEL_MODULE)
+static u64 lcdc_dmamask = DMA_BIT_MASK(32);
+static struct atmel_lcdfb_info lcdc_data;
+
+static struct resource lcdc_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_LCDC_BASE,
+ .end = AT91SAM9G45_LCDC_BASE + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_LCDC,
+ .end = AT91SAM9G45_ID_LCDC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91_lcdc_device = {
+ .name = "atmel_lcdfb",
+ .id = 0,
+ .dev = {
+ .dma_mask = &lcdc_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &lcdc_data,
+ },
+ .resource = lcdc_resources,
+ .num_resources = ARRAY_SIZE(lcdc_resources),
+};
+
+void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data)
+{
+ if (!data)
+ return;
+
+ at91_set_A_periph(AT91_PIN_PE0, 0); /* LCDDPWR */
+
+ at91_set_A_periph(AT91_PIN_PE2, 0); /* LCDCC */
+ at91_set_A_periph(AT91_PIN_PE3, 0); /* LCDVSYNC */
+ at91_set_A_periph(AT91_PIN_PE4, 0); /* LCDHSYNC */
+ at91_set_A_periph(AT91_PIN_PE5, 0); /* LCDDOTCK */
+ at91_set_A_periph(AT91_PIN_PE6, 0); /* LCDDEN */
+ at91_set_A_periph(AT91_PIN_PE7, 0); /* LCDD0 */
+ at91_set_A_periph(AT91_PIN_PE8, 0); /* LCDD1 */
+ at91_set_A_periph(AT91_PIN_PE9, 0); /* LCDD2 */
+ at91_set_A_periph(AT91_PIN_PE10, 0); /* LCDD3 */
+ at91_set_A_periph(AT91_PIN_PE11, 0); /* LCDD4 */
+ at91_set_A_periph(AT91_PIN_PE12, 0); /* LCDD5 */
+ at91_set_A_periph(AT91_PIN_PE13, 0); /* LCDD6 */
+ at91_set_A_periph(AT91_PIN_PE14, 0); /* LCDD7 */
+ at91_set_A_periph(AT91_PIN_PE15, 0); /* LCDD8 */
+ at91_set_A_periph(AT91_PIN_PE16, 0); /* LCDD9 */
+ at91_set_A_periph(AT91_PIN_PE17, 0); /* LCDD10 */
+ at91_set_A_periph(AT91_PIN_PE18, 0); /* LCDD11 */
+ at91_set_A_periph(AT91_PIN_PE19, 0); /* LCDD12 */
+ at91_set_A_periph(AT91_PIN_PE20, 0); /* LCDD13 */
+ at91_set_A_periph(AT91_PIN_PE21, 0); /* LCDD14 */
+ at91_set_A_periph(AT91_PIN_PE22, 0); /* LCDD15 */
+ at91_set_A_periph(AT91_PIN_PE23, 0); /* LCDD16 */
+ at91_set_A_periph(AT91_PIN_PE24, 0); /* LCDD17 */
+ at91_set_A_periph(AT91_PIN_PE25, 0); /* LCDD18 */
+ at91_set_A_periph(AT91_PIN_PE26, 0); /* LCDD19 */
+ at91_set_A_periph(AT91_PIN_PE27, 0); /* LCDD20 */
+ at91_set_A_periph(AT91_PIN_PE28, 0); /* LCDD21 */
+ at91_set_A_periph(AT91_PIN_PE29, 0); /* LCDD22 */
+ at91_set_A_periph(AT91_PIN_PE30, 0); /* LCDD23 */
+
+ lcdc_data = *data;
+ platform_device_register(&at91_lcdc_device);
+}
+#else
+void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * Timer/Counter block
+ * -------------------------------------------------------------------- */
+
+#ifdef CONFIG_ATMEL_TCLIB
+static struct resource tcb0_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_TCB0,
+ .end = AT91SAM9G45_BASE_TCB0 + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_TCB,
+ .end = AT91SAM9G45_ID_TCB,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91sam9g45_tcb0_device = {
+ .name = "atmel_tcb",
+ .id = 0,
+ .resource = tcb0_resources,
+ .num_resources = ARRAY_SIZE(tcb0_resources),
+};
+
+/* TCB1 begins with TC3 */
+static struct resource tcb1_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_TCB1,
+ .end = AT91SAM9G45_BASE_TCB1 + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_TCB,
+ .end = AT91SAM9G45_ID_TCB,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91sam9g45_tcb1_device = {
+ .name = "atmel_tcb",
+ .id = 1,
+ .resource = tcb1_resources,
+ .num_resources = ARRAY_SIZE(tcb1_resources),
+};
+
+static void __init at91_add_device_tc(void)
+{
+ /* this chip has one clock and irq for all six TC channels */
+ at91_clock_associate("tcb_clk", &at91sam9g45_tcb0_device.dev, "t0_clk");
+ platform_device_register(&at91sam9g45_tcb0_device);
+ at91_clock_associate("tcb_clk", &at91sam9g45_tcb1_device.dev, "t0_clk");
+ platform_device_register(&at91sam9g45_tcb1_device);
+}
+#else
+static void __init at91_add_device_tc(void) { }
+#endif
+
+
+/* --------------------------------------------------------------------
+ * RTC
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_RTC_DRV_AT91RM9200) || defined(CONFIG_RTC_DRV_AT91RM9200_MODULE)
+static struct platform_device at91sam9g45_rtc_device = {
+ .name = "at91_rtc",
+ .id = -1,
+ .num_resources = 0,
+};
+
+static void __init at91_add_device_rtc(void)
+{
+ platform_device_register(&at91sam9g45_rtc_device);
+}
+#else
+static void __init at91_add_device_rtc(void) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * RTT
+ * -------------------------------------------------------------------- */
+
+static struct resource rtt_resources[] = {
+ {
+ .start = AT91_BASE_SYS + AT91_RTT,
+ .end = AT91_BASE_SYS + AT91_RTT + SZ_16 - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device at91sam9g45_rtt_device = {
+ .name = "at91_rtt",
+ .id = 0,
+ .resource = rtt_resources,
+ .num_resources = ARRAY_SIZE(rtt_resources),
+};
+
+static void __init at91_add_device_rtt(void)
+{
+ platform_device_register(&at91sam9g45_rtt_device);
+}
+
+
+/* --------------------------------------------------------------------
+ * Watchdog
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_AT91SAM9_WATCHDOG) || defined(CONFIG_AT91SAM9_WATCHDOG_MODULE)
+static struct platform_device at91sam9g45_wdt_device = {
+ .name = "at91_wdt",
+ .id = -1,
+ .num_resources = 0,
+};
+
+static void __init at91_add_device_watchdog(void)
+{
+ platform_device_register(&at91sam9g45_wdt_device);
+}
+#else
+static void __init at91_add_device_watchdog(void) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * PWM
+ * --------------------------------------------------------------------*/
+
+#if defined(CONFIG_ATMEL_PWM) || defined(CONFIG_ATMEL_PWM_MODULE)
+static u32 pwm_mask;
+
+static struct resource pwm_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_PWMC,
+ .end = AT91SAM9G45_BASE_PWMC + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_PWMC,
+ .end = AT91SAM9G45_ID_PWMC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91sam9g45_pwm0_device = {
+ .name = "atmel_pwm",
+ .id = -1,
+ .dev = {
+ .platform_data = &pwm_mask,
+ },
+ .resource = pwm_resources,
+ .num_resources = ARRAY_SIZE(pwm_resources),
+};
+
+void __init at91_add_device_pwm(u32 mask)
+{
+ if (mask & (1 << AT91_PWM0))
+ at91_set_B_periph(AT91_PIN_PD24, 1); /* enable PWM0 */
+
+ if (mask & (1 << AT91_PWM1))
+ at91_set_B_periph(AT91_PIN_PD31, 1); /* enable PWM1 */
+
+ if (mask & (1 << AT91_PWM2))
+ at91_set_B_periph(AT91_PIN_PD26, 1); /* enable PWM2 */
+
+ if (mask & (1 << AT91_PWM3))
+ at91_set_B_periph(AT91_PIN_PD0, 1); /* enable PWM3 */
+
+ pwm_mask = mask;
+
+ platform_device_register(&at91sam9g45_pwm0_device);
+}
+#else
+void __init at91_add_device_pwm(u32 mask) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * SSC -- Synchronous Serial Controller
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_ATMEL_SSC) || defined(CONFIG_ATMEL_SSC_MODULE)
+static u64 ssc0_dmamask = DMA_BIT_MASK(32);
+
+static struct resource ssc0_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_SSC0,
+ .end = AT91SAM9G45_BASE_SSC0 + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_SSC0,
+ .end = AT91SAM9G45_ID_SSC0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91sam9g45_ssc0_device = {
+ .name = "ssc",
+ .id = 0,
+ .dev = {
+ .dma_mask = &ssc0_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .resource = ssc0_resources,
+ .num_resources = ARRAY_SIZE(ssc0_resources),
+};
+
+static inline void configure_ssc0_pins(unsigned pins)
+{
+ if (pins & ATMEL_SSC_TF)
+ at91_set_A_periph(AT91_PIN_PD1, 1);
+ if (pins & ATMEL_SSC_TK)
+ at91_set_A_periph(AT91_PIN_PD0, 1);
+ if (pins & ATMEL_SSC_TD)
+ at91_set_A_periph(AT91_PIN_PD2, 1);
+ if (pins & ATMEL_SSC_RD)
+ at91_set_A_periph(AT91_PIN_PD3, 1);
+ if (pins & ATMEL_SSC_RK)
+ at91_set_A_periph(AT91_PIN_PD4, 1);
+ if (pins & ATMEL_SSC_RF)
+ at91_set_A_periph(AT91_PIN_PD5, 1);
+}
+
+static u64 ssc1_dmamask = DMA_BIT_MASK(32);
+
+static struct resource ssc1_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_SSC1,
+ .end = AT91SAM9G45_BASE_SSC1 + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_SSC1,
+ .end = AT91SAM9G45_ID_SSC1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device at91sam9g45_ssc1_device = {
+ .name = "ssc",
+ .id = 1,
+ .dev = {
+ .dma_mask = &ssc1_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .resource = ssc1_resources,
+ .num_resources = ARRAY_SIZE(ssc1_resources),
+};
+
+static inline void configure_ssc1_pins(unsigned pins)
+{
+ if (pins & ATMEL_SSC_TF)
+ at91_set_A_periph(AT91_PIN_PD14, 1);
+ if (pins & ATMEL_SSC_TK)
+ at91_set_A_periph(AT91_PIN_PD12, 1);
+ if (pins & ATMEL_SSC_TD)
+ at91_set_A_periph(AT91_PIN_PD10, 1);
+ if (pins & ATMEL_SSC_RD)
+ at91_set_A_periph(AT91_PIN_PD11, 1);
+ if (pins & ATMEL_SSC_RK)
+ at91_set_A_periph(AT91_PIN_PD13, 1);
+ if (pins & ATMEL_SSC_RF)
+ at91_set_A_periph(AT91_PIN_PD15, 1);
+}
+
+/*
+ * SSC controllers are accessed through library code, instead of any
+ * kind of all-singing/all-dancing driver. For example one could be
+ * used by a particular I2S audio codec's driver, while another one
+ * on the same system might be used by a custom data capture driver.
+ */
+void __init at91_add_device_ssc(unsigned id, unsigned pins)
+{
+ struct platform_device *pdev;
+
+ /*
+ * NOTE: caller is responsible for passing information matching
+ * "pins" to whatever will be using each particular controller.
+ */
+ switch (id) {
+ case AT91SAM9G45_ID_SSC0:
+ pdev = &at91sam9g45_ssc0_device;
+ configure_ssc0_pins(pins);
+ at91_clock_associate("ssc0_clk", &pdev->dev, "pclk");
+ break;
+ case AT91SAM9G45_ID_SSC1:
+ pdev = &at91sam9g45_ssc1_device;
+ configure_ssc1_pins(pins);
+ at91_clock_associate("ssc1_clk", &pdev->dev, "pclk");
+ break;
+ default:
+ return;
+ }
+
+ platform_device_register(pdev);
+}
+
+#else
+void __init at91_add_device_ssc(unsigned id, unsigned pins) {}
+#endif
+
+
+/* --------------------------------------------------------------------
+ * UART
+ * -------------------------------------------------------------------- */
+
+#if defined(CONFIG_SERIAL_ATMEL)
+static struct resource dbgu_resources[] = {
+ [0] = {
+ .start = AT91_VA_BASE_SYS + AT91_DBGU,
+ .end = AT91_VA_BASE_SYS + AT91_DBGU + SZ_512 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91_ID_SYS,
+ .end = AT91_ID_SYS,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct atmel_uart_data dbgu_data = {
+ .use_dma_tx = 0,
+ .use_dma_rx = 0,
+ .regs = (void __iomem *)(AT91_VA_BASE_SYS + AT91_DBGU),
+};
+
+static u64 dbgu_dmamask = DMA_BIT_MASK(32);
+
+static struct platform_device at91sam9g45_dbgu_device = {
+ .name = "atmel_usart",
+ .id = 0,
+ .dev = {
+ .dma_mask = &dbgu_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &dbgu_data,
+ },
+ .resource = dbgu_resources,
+ .num_resources = ARRAY_SIZE(dbgu_resources),
+};
+
+static inline void configure_dbgu_pins(void)
+{
+ at91_set_A_periph(AT91_PIN_PB12, 0); /* DRXD */
+ at91_set_A_periph(AT91_PIN_PB13, 1); /* DTXD */
+}
+
+static struct resource uart0_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_US0,
+ .end = AT91SAM9G45_BASE_US0 + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_US0,
+ .end = AT91SAM9G45_ID_US0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct atmel_uart_data uart0_data = {
+ .use_dma_tx = 1,
+ .use_dma_rx = 1,
+};
+
+static u64 uart0_dmamask = DMA_BIT_MASK(32);
+
+static struct platform_device at91sam9g45_uart0_device = {
+ .name = "atmel_usart",
+ .id = 1,
+ .dev = {
+ .dma_mask = &uart0_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &uart0_data,
+ },
+ .resource = uart0_resources,
+ .num_resources = ARRAY_SIZE(uart0_resources),
+};
+
+static inline void configure_usart0_pins(unsigned pins)
+{
+ at91_set_A_periph(AT91_PIN_PB19, 1); /* TXD0 */
+ at91_set_A_periph(AT91_PIN_PB18, 0); /* RXD0 */
+
+ if (pins & ATMEL_UART_RTS)
+ at91_set_B_periph(AT91_PIN_PB17, 0); /* RTS0 */
+ if (pins & ATMEL_UART_CTS)
+ at91_set_B_periph(AT91_PIN_PB15, 0); /* CTS0 */
+}
+
+static struct resource uart1_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_US1,
+ .end = AT91SAM9G45_BASE_US1 + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_US1,
+ .end = AT91SAM9G45_ID_US1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct atmel_uart_data uart1_data = {
+ .use_dma_tx = 1,
+ .use_dma_rx = 1,
+};
+
+static u64 uart1_dmamask = DMA_BIT_MASK(32);
+
+static struct platform_device at91sam9g45_uart1_device = {
+ .name = "atmel_usart",
+ .id = 2,
+ .dev = {
+ .dma_mask = &uart1_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &uart1_data,
+ },
+ .resource = uart1_resources,
+ .num_resources = ARRAY_SIZE(uart1_resources),
+};
+
+static inline void configure_usart1_pins(unsigned pins)
+{
+ at91_set_A_periph(AT91_PIN_PB4, 1); /* TXD1 */
+ at91_set_A_periph(AT91_PIN_PB5, 0); /* RXD1 */
+
+ if (pins & ATMEL_UART_RTS)
+ at91_set_A_periph(AT91_PIN_PD16, 0); /* RTS1 */
+ if (pins & ATMEL_UART_CTS)
+ at91_set_A_periph(AT91_PIN_PD17, 0); /* CTS1 */
+}
+
+static struct resource uart2_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_US2,
+ .end = AT91SAM9G45_BASE_US2 + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_US2,
+ .end = AT91SAM9G45_ID_US2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct atmel_uart_data uart2_data = {
+ .use_dma_tx = 1,
+ .use_dma_rx = 1,
+};
+
+static u64 uart2_dmamask = DMA_BIT_MASK(32);
+
+static struct platform_device at91sam9g45_uart2_device = {
+ .name = "atmel_usart",
+ .id = 3,
+ .dev = {
+ .dma_mask = &uart2_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &uart2_data,
+ },
+ .resource = uart2_resources,
+ .num_resources = ARRAY_SIZE(uart2_resources),
+};
+
+static inline void configure_usart2_pins(unsigned pins)
+{
+ at91_set_A_periph(AT91_PIN_PB6, 1); /* TXD2 */
+ at91_set_A_periph(AT91_PIN_PB7, 0); /* RXD2 */
+
+ if (pins & ATMEL_UART_RTS)
+ at91_set_B_periph(AT91_PIN_PC9, 0); /* RTS2 */
+ if (pins & ATMEL_UART_CTS)
+ at91_set_B_periph(AT91_PIN_PC11, 0); /* CTS2 */
+}
+
+static struct resource uart3_resources[] = {
+ [0] = {
+ .start = AT91SAM9G45_BASE_US3,
+ .end = AT91SAM9G45_BASE_US3 + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = AT91SAM9G45_ID_US3,
+ .end = AT91SAM9G45_ID_US3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct atmel_uart_data uart3_data = {
+ .use_dma_tx = 1,
+ .use_dma_rx = 1,
+};
+
+static u64 uart3_dmamask = DMA_BIT_MASK(32);
+
+static struct platform_device at91sam9g45_uart3_device = {
+ .name = "atmel_usart",
+ .id = 4,
+ .dev = {
+ .dma_mask = &uart3_dmamask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .platform_data = &uart3_data,
+ },
+ .resource = uart3_resources,
+ .num_resources = ARRAY_SIZE(uart3_resources),
+};
+
+static inline void configure_usart3_pins(unsigned pins)
+{
+ at91_set_A_periph(AT91_PIN_PB8, 1); /* TXD3 */
+ at91_set_A_periph(AT91_PIN_PB9, 0); /* RXD3 */
+
+ if (pins & ATMEL_UART_RTS)
+ at91_set_B_periph(AT91_PIN_PA23, 0); /* RTS3 */
+ if (pins & ATMEL_UART_CTS)
+ at91_set_B_periph(AT91_PIN_PA24, 0); /* CTS3 */
+}
+
+static struct platform_device *__initdata at91_uarts[ATMEL_MAX_UART]; /* the UARTs to use */
+struct platform_device *atmel_default_console_device; /* the serial console device */
+
+void __init at91_register_uart(unsigned id, unsigned portnr, unsigned pins)
+{
+ struct platform_device *pdev;
+
+ switch (id) {
+ case 0: /* DBGU */
+ pdev = &at91sam9g45_dbgu_device;
+ configure_dbgu_pins();
+ at91_clock_associate("mck", &pdev->dev, "usart");
+ break;
+ case AT91SAM9G45_ID_US0:
+ pdev = &at91sam9g45_uart0_device;
+ configure_usart0_pins(pins);
+ at91_clock_associate("usart0_clk", &pdev->dev, "usart");
+ break;
+ case AT91SAM9G45_ID_US1:
+ pdev = &at91sam9g45_uart1_device;
+ configure_usart1_pins(pins);
+ at91_clock_associate("usart1_clk", &pdev->dev, "usart");
+ break;
+ case AT91SAM9G45_ID_US2:
+ pdev = &at91sam9g45_uart2_device;
+ configure_usart2_pins(pins);
+ at91_clock_associate("usart2_clk", &pdev->dev, "usart");
+ break;
+ case AT91SAM9G45_ID_US3:
+ pdev = &at91sam9g45_uart3_device;
+ configure_usart3_pins(pins);
+ at91_clock_associate("usart3_clk", &pdev->dev, "usart");
+ break;
+ default:
+ return;
+ }
+ pdev->id = portnr; /* update to mapped ID */
+
+ if (portnr < ATMEL_MAX_UART)
+ at91_uarts[portnr] = pdev;
+}
+
+void __init at91_set_serial_console(unsigned portnr)
+{
+ if (portnr < ATMEL_MAX_UART)
+ atmel_default_console_device = at91_uarts[portnr];
+}
+
+void __init at91_add_device_serial(void)
+{
+ int i;
+
+ for (i = 0; i < ATMEL_MAX_UART; i++) {
+ if (at91_uarts[i])
+ platform_device_register(at91_uarts[i]);
+ }
+
+ if (!atmel_default_console_device)
+ printk(KERN_INFO "AT91: No default serial console defined.\n");
+}
+#else
+void __init at91_register_uart(unsigned id, unsigned portnr, unsigned pins) {}
+void __init at91_set_serial_console(unsigned portnr) {}
+void __init at91_add_device_serial(void) {}
+#endif
+
+
+/* -------------------------------------------------------------------- */
+/*
+ * These devices are always present and don't need any board-specific
+ * setup.
+ */
+static int __init at91_add_standard_devices(void)
+{
+ at91_add_device_rtc();
+ at91_add_device_rtt();
+ at91_add_device_watchdog();
+ at91_add_device_tc();
+ return 0;
+}
+
+arch_initcall(at91_add_standard_devices);
diff --git a/arch/arm/mach-at91/board-afeb-9260v1.c b/arch/arm/mach-at91/board-afeb-9260v1.c
index 970fd6b6753..61e52b66bc7 100644
--- a/arch/arm/mach-at91/board-afeb-9260v1.c
+++ b/arch/arm/mach-at91/board-afeb-9260v1.c
@@ -174,6 +174,16 @@ static struct i2c_board_info __initdata afeb9260_i2c_devices[] = {
},
};
+/*
+ * IDE (CF True IDE mode)
+ */
+static struct at91_cf_data afeb9260_cf_data = {
+ .chipselect = 4,
+ .irq_pin = AT91_PIN_PA6,
+ .rst_pin = AT91_PIN_PA7,
+ .flags = AT91_CF_TRUE_IDE,
+};
+
static void __init afeb9260_board_init(void)
{
/* Serial */
@@ -202,6 +212,8 @@ static void __init afeb9260_board_init(void)
ARRAY_SIZE(afeb9260_i2c_devices));
/* Audio */
at91_add_device_ssc(AT91SAM9260_ID_SSC, ATMEL_SSC_TX);
+ /* IDE */
+ at91_add_device_cf(&afeb9260_cf_data);
}
MACHINE_START(AFEB9260, "Custom afeb9260 board")
diff --git a/arch/arm/mach-at91/board-cpu9krea.c b/arch/arm/mach-at91/board-cpu9krea.c
new file mode 100644
index 00000000000..4bc2e9f6ebb
--- /dev/null
+++ b/arch/arm/mach-at91/board-cpu9krea.c
@@ -0,0 +1,385 @@
+/*
+ * linux/arch/arm/mach-at91/board-cpu9krea.c
+ *
+ * Copyright (C) 2005 SAN People
+ * Copyright (C) 2006 Atmel
+ * Copyright (C) 2009 Eric Benard - eric@eukrea.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/mtd/physmap.h>
+
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/at91sam9_smc.h>
+#include <mach/at91sam9260_matrix.h>
+
+#include "sam9_smc.h"
+#include "generic.h"
+
+static void __init cpu9krea_map_io(void)
+{
+ /* Initialize processor: 18.432 MHz crystal */
+ at91sam9260_initialize(18432000);
+
+ /* DGBU on ttyS0. (Rx & Tx only) */
+ at91_register_uart(0, 0, 0);
+
+ /* USART0 on ttyS1. (Rx, Tx, CTS, RTS, DTR, DSR, DCD, RI) */
+ at91_register_uart(AT91SAM9260_ID_US0, 1, ATMEL_UART_CTS |
+ ATMEL_UART_RTS | ATMEL_UART_DTR | ATMEL_UART_DSR |
+ ATMEL_UART_DCD | ATMEL_UART_RI);
+
+ /* USART1 on ttyS2. (Rx, Tx, RTS, CTS) */
+ at91_register_uart(AT91SAM9260_ID_US1, 2, ATMEL_UART_CTS |
+ ATMEL_UART_RTS);
+
+ /* USART2 on ttyS3. (Rx, Tx, RTS, CTS) */
+ at91_register_uart(AT91SAM9260_ID_US2, 3, ATMEL_UART_CTS |
+ ATMEL_UART_RTS);
+
+ /* USART3 on ttyS4. (Rx, Tx) */
+ at91_register_uart(AT91SAM9260_ID_US3, 4, 0);
+
+ /* USART4 on ttyS5. (Rx, Tx) */
+ at91_register_uart(AT91SAM9260_ID_US4, 5, 0);
+
+ /* USART5 on ttyS6. (Rx, Tx) */
+ at91_register_uart(AT91SAM9260_ID_US5, 6, 0);
+
+ /* set serial console to ttyS0 (ie, DBGU) */
+ at91_set_serial_console(0);
+}
+
+static void __init cpu9krea_init_irq(void)
+{
+ at91sam9260_init_interrupts(NULL);
+}
+
+/*
+ * USB Host port
+ */
+static struct at91_usbh_data __initdata cpu9krea_usbh_data = {
+ .ports = 2,
+};
+
+/*
+ * USB Device port
+ */
+static struct at91_udc_data __initdata cpu9krea_udc_data = {
+ .vbus_pin = AT91_PIN_PC8,
+ .pullup_pin = 0, /* pull-up driven by UDC */
+};
+
+/*
+ * MACB Ethernet device
+ */
+static struct at91_eth_data __initdata cpu9krea_macb_data = {
+ .is_rmii = 1,
+};
+
+/*
+ * NAND flash
+ */
+static struct atmel_nand_data __initdata cpu9krea_nand_data = {
+ .ale = 21,
+ .cle = 22,
+ .rdy_pin = AT91_PIN_PC13,
+ .enable_pin = AT91_PIN_PC14,
+ .bus_width_16 = 0,
+};
+
+#ifdef CONFIG_MACH_CPU9260
+static struct sam9_smc_config __initdata cpu9krea_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 3,
+ .nrd_pulse = 3,
+ .ncs_write_pulse = 3,
+ .nwe_pulse = 3,
+
+ .read_cycle = 5,
+ .write_cycle = 5,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE
+ | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_DBW_8,
+ .tdf_cycles = 2,
+};
+#else
+static struct sam9_smc_config __initdata cpu9krea_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 2,
+ .ncs_write_setup = 0,
+ .nwe_setup = 2,
+
+ .ncs_read_pulse = 4,
+ .nrd_pulse = 4,
+ .ncs_write_pulse = 4,
+ .nwe_pulse = 4,
+
+ .read_cycle = 7,
+ .write_cycle = 7,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE
+ | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_DBW_8,
+ .tdf_cycles = 3,
+};
+#endif
+
+static void __init cpu9krea_add_device_nand(void)
+{
+ sam9_smc_configure(3, &cpu9krea_nand_smc_config);
+ at91_add_device_nand(&cpu9krea_nand_data);
+}
+
+/*
+ * NOR flash
+ */
+static struct physmap_flash_data cpuat9260_nor_data = {
+ .width = 2,
+};
+
+#define NOR_BASE AT91_CHIPSELECT_0
+#define NOR_SIZE SZ_64M
+
+static struct resource nor_flash_resources[] = {
+ {
+ .start = NOR_BASE,
+ .end = NOR_BASE + NOR_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device cpu9krea_nor_flash = {
+ .name = "physmap-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &cpuat9260_nor_data,
+ },
+ .resource = nor_flash_resources,
+ .num_resources = ARRAY_SIZE(nor_flash_resources),
+};
+
+#ifdef CONFIG_MACH_CPU9260
+static struct sam9_smc_config __initdata cpu9krea_nor_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 10,
+ .nrd_pulse = 10,
+ .ncs_write_pulse = 6,
+ .nwe_pulse = 6,
+
+ .read_cycle = 12,
+ .write_cycle = 8,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE
+ | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_BAT_WRITE
+ | AT91_SMC_DBW_16,
+ .tdf_cycles = 2,
+};
+#else
+static struct sam9_smc_config __initdata cpu9krea_nor_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 1,
+ .ncs_write_setup = 0,
+ .nwe_setup = 1,
+
+ .ncs_read_pulse = 13,
+ .nrd_pulse = 13,
+ .ncs_write_pulse = 8,
+ .nwe_pulse = 8,
+
+ .read_cycle = 15,
+ .write_cycle = 10,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE
+ | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_BAT_WRITE
+ | AT91_SMC_DBW_16,
+ .tdf_cycles = 2,
+};
+#endif
+
+static __init void cpu9krea_add_device_nor(void)
+{
+ unsigned long csa;
+
+ csa = at91_sys_read(AT91_MATRIX_EBICSA);
+ at91_sys_write(AT91_MATRIX_EBICSA, csa | AT91_MATRIX_VDDIOMSEL_3_3V);
+
+ /* configure chip-select 0 (NOR) */
+ sam9_smc_configure(0, &cpu9krea_nor_smc_config);
+
+ platform_device_register(&cpu9krea_nor_flash);
+}
+
+/*
+ * LEDs
+ */
+static struct gpio_led cpu9krea_leds[] = {
+ { /* LED1 */
+ .name = "LED1",
+ .gpio = AT91_PIN_PC11,
+ .active_low = 1,
+ .default_trigger = "timer",
+ },
+ { /* LED2 */
+ .name = "LED2",
+ .gpio = AT91_PIN_PC12,
+ .active_low = 1,
+ .default_trigger = "heartbeat",
+ },
+ { /* LED3 */
+ .name = "LED3",
+ .gpio = AT91_PIN_PC7,
+ .active_low = 1,
+ .default_trigger = "none",
+ },
+ { /* LED4 */
+ .name = "LED4",
+ .gpio = AT91_PIN_PC9,
+ .active_low = 1,
+ .default_trigger = "none",
+ }
+};
+
+static struct i2c_board_info __initdata cpu9krea_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("rtc-ds1307", 0x68),
+ .type = "ds1339",
+ },
+};
+
+/*
+ * GPIO Buttons
+ */
+#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
+static struct gpio_keys_button cpu9krea_buttons[] = {
+ {
+ .gpio = AT91_PIN_PC3,
+ .code = BTN_0,
+ .desc = "BP1",
+ .active_low = 1,
+ .wakeup = 1,
+ },
+ {
+ .gpio = AT91_PIN_PB20,
+ .code = BTN_1,
+ .desc = "BP2",
+ .active_low = 1,
+ .wakeup = 1,
+ }
+};
+
+static struct gpio_keys_platform_data cpu9krea_button_data = {
+ .buttons = cpu9krea_buttons,
+ .nbuttons = ARRAY_SIZE(cpu9krea_buttons),
+};
+
+static struct platform_device cpu9krea_button_device = {
+ .name = "gpio-keys",
+ .id = -1,
+ .num_resources = 0,
+ .dev = {
+ .platform_data = &cpu9krea_button_data,
+ }
+};
+
+static void __init cpu9krea_add_device_buttons(void)
+{
+ at91_set_gpio_input(AT91_PIN_PC3, 1); /* BP1 */
+ at91_set_deglitch(AT91_PIN_PC3, 1);
+ at91_set_gpio_input(AT91_PIN_PB20, 1); /* BP2 */
+ at91_set_deglitch(AT91_PIN_PB20, 1);
+
+ platform_device_register(&cpu9krea_button_device);
+}
+#else
+static void __init cpu9krea_add_device_buttons(void)
+{
+}
+#endif
+
+/*
+ * MCI (SD/MMC)
+ */
+static struct at91_mmc_data __initdata cpu9krea_mmc_data = {
+ .slot_b = 0,
+ .wire4 = 1,
+ .det_pin = AT91_PIN_PA29,
+};
+
+static void __init cpu9krea_board_init(void)
+{
+ /* NOR */
+ cpu9krea_add_device_nor();
+ /* Serial */
+ at91_add_device_serial();
+ /* USB Host */
+ at91_add_device_usbh(&cpu9krea_usbh_data);
+ /* USB Device */
+ at91_add_device_udc(&cpu9krea_udc_data);
+ /* NAND */
+ cpu9krea_add_device_nand();
+ /* Ethernet */
+ at91_add_device_eth(&cpu9krea_macb_data);
+ /* MMC */
+ at91_add_device_mmc(0, &cpu9krea_mmc_data);
+ /* I2C */
+ at91_add_device_i2c(cpu9krea_i2c_devices,
+ ARRAY_SIZE(cpu9krea_i2c_devices));
+ /* LEDs */
+ at91_gpio_leds(cpu9krea_leds, ARRAY_SIZE(cpu9krea_leds));
+ /* Push Buttons */
+ cpu9krea_add_device_buttons();
+}
+
+#ifdef CONFIG_MACH_CPU9260
+MACHINE_START(CPUAT9260, "Eukrea CPU9260")
+#else
+MACHINE_START(CPUAT9G20, "Eukrea CPU9G20")
+#endif
+ /* Maintainer: Eric Benard - EUKREA Electromatique */
+ .phys_io = AT91_BASE_SYS,
+ .io_pg_offst = (AT91_VA_BASE_SYS >> 18) & 0xfffc,
+ .boot_params = AT91_SDRAM_BASE + 0x100,
+ .timer = &at91sam926x_timer,
+ .map_io = cpu9krea_map_io,
+ .init_irq = cpu9krea_init_irq,
+ .init_machine = cpu9krea_board_init,
+MACHINE_END
diff --git a/arch/arm/mach-at91/board-cpuat91.c b/arch/arm/mach-at91/board-cpuat91.c
new file mode 100644
index 00000000000..a28d9965619
--- /dev/null
+++ b/arch/arm/mach-at91/board-cpuat91.c
@@ -0,0 +1,185 @@
+/*
+ * linux/arch/arm/mach-at91/board-cpuat91.c
+ *
+ * Copyright (C) 2009 Eric Benard - eric@eukrea.com
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/physmap.h>
+#include <linux/mtd/plat-ram.h>
+
+#include <mach/hardware.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/at91rm9200_mc.h>
+
+#include "generic.h"
+
+static struct gpio_led cpuat91_leds[] = {
+ {
+ .name = "led1",
+ .default_trigger = "heartbeat",
+ .active_low = 1,
+ .gpio = AT91_PIN_PC0,
+ },
+};
+
+static void __init cpuat91_map_io(void)
+{
+ /* Initialize processor: 18.432 MHz crystal */
+ at91rm9200_initialize(18432000, AT91RM9200_PQFP);
+
+ /* DBGU on ttyS0. (Rx & Tx only) */
+ at91_register_uart(0, 0, 0);
+
+ /* USART0 on ttyS1. (Rx, Tx, CTS, RTS) */
+ at91_register_uart(AT91RM9200_ID_US0, 1, ATMEL_UART_CTS |
+ ATMEL_UART_RTS);
+
+ /* USART1 on ttyS2. (Rx, Tx, CTS, RTS, DTR, DSR, DCD, RI) */
+ at91_register_uart(AT91RM9200_ID_US1, 2, ATMEL_UART_CTS |
+ ATMEL_UART_RTS | ATMEL_UART_DTR | ATMEL_UART_DSR |
+ ATMEL_UART_DCD | ATMEL_UART_RI);
+
+ /* USART2 on ttyS3 (Rx, Tx) */
+ at91_register_uart(AT91RM9200_ID_US2, 3, 0);
+
+ /* USART3 on ttyS4 (Rx, Tx, CTS, RTS) */
+ at91_register_uart(AT91RM9200_ID_US3, 4, ATMEL_UART_CTS |
+ ATMEL_UART_RTS);
+
+ /* set serial console to ttyS0 (ie, DBGU) */
+ at91_set_serial_console(0);
+}
+
+static void __init cpuat91_init_irq(void)
+{
+ at91rm9200_init_interrupts(NULL);
+}
+
+static struct at91_eth_data __initdata cpuat91_eth_data = {
+ .is_rmii = 1,
+};
+
+static struct at91_usbh_data __initdata cpuat91_usbh_data = {
+ .ports = 1,
+};
+
+static struct at91_udc_data __initdata cpuat91_udc_data = {
+ .vbus_pin = AT91_PIN_PC15,
+ .pullup_pin = AT91_PIN_PC14,
+};
+
+static struct at91_mmc_data __initdata cpuat91_mmc_data = {
+ .det_pin = AT91_PIN_PC2,
+ .wire4 = 1,
+};
+
+static struct physmap_flash_data cpuat91_flash_data = {
+ .width = 2,
+};
+
+static struct resource cpuat91_flash_resource = {
+ .start = AT91_CHIPSELECT_0,
+ .end = AT91_CHIPSELECT_0 + SZ_16M - 1,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device cpuat91_norflash = {
+ .name = "physmap-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &cpuat91_flash_data,
+ },
+ .resource = &cpuat91_flash_resource,
+ .num_resources = 1,
+};
+
+#ifdef CONFIG_MTD_PLATRAM
+struct platdata_mtd_ram at91_sram_pdata = {
+ .mapname = "SRAM",
+ .bankwidth = 2,
+};
+
+static struct resource at91_sram_resource[] = {
+ [0] = {
+ .start = AT91RM9200_SRAM_BASE,
+ .end = AT91RM9200_SRAM_BASE + AT91RM9200_SRAM_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device at91_sram = {
+ .name = "mtd-ram",
+ .id = 0,
+ .resource = at91_sram_resource,
+ .num_resources = ARRAY_SIZE(at91_sram_resource),
+ .dev = {
+ .platform_data = &at91_sram_pdata,
+ },
+};
+#endif /* MTD_PLATRAM */
+
+static struct platform_device *platform_devices[] __initdata = {
+ &cpuat91_norflash,
+#ifdef CONFIG_MTD_PLATRAM
+ &at91_sram,
+#endif /* CONFIG_MTD_PLATRAM */
+};
+
+static void __init cpuat91_board_init(void)
+{
+ /* Serial */
+ at91_add_device_serial();
+ /* LEDs. */
+ at91_gpio_leds(cpuat91_leds, ARRAY_SIZE(cpuat91_leds));
+ /* Ethernet */
+ at91_add_device_eth(&cpuat91_eth_data);
+ /* USB Host */
+ at91_add_device_usbh(&cpuat91_usbh_data);
+ /* USB Device */
+ at91_add_device_udc(&cpuat91_udc_data);
+ /* MMC */
+ at91_add_device_mmc(0, &cpuat91_mmc_data);
+ /* I2C */
+ at91_add_device_i2c(NULL, 0);
+ /* Platform devices */
+ platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));
+}
+
+MACHINE_START(CPUAT91, "Eukrea")
+ /* Maintainer: Eric Benard - EUKREA Electromatique */
+ .phys_io = AT91_BASE_SYS,
+ .io_pg_offst = (AT91_VA_BASE_SYS >> 18) & 0xfffc,
+ .boot_params = AT91_SDRAM_BASE + 0x100,
+ .timer = &at91rm9200_timer,
+ .map_io = cpuat91_map_io,
+ .init_irq = cpuat91_init_irq,
+ .init_machine = cpuat91_board_init,
+MACHINE_END
diff --git a/arch/arm/mach-at91/board-sam9261ek.c b/arch/arm/mach-at91/board-sam9261ek.c
index d5266da5531..f9b19993a7a 100644
--- a/arch/arm/mach-at91/board-sam9261ek.c
+++ b/arch/arm/mach-at91/board-sam9261ek.c
@@ -287,7 +287,11 @@ static void __init ek_add_device_ts(void) {}
*/
static struct at73c213_board_info at73c213_data = {
.ssc_id = 1,
+#if defined(CONFIG_MACH_AT91SAM9261EK)
.shortname = "AT91SAM9261-EK external DAC",
+#else
+ .shortname = "AT91SAM9G10-EK external DAC",
+#endif
};
#if defined(CONFIG_SND_AT73C213) || defined(CONFIG_SND_AT73C213_MODULE)
@@ -414,6 +418,9 @@ static struct atmel_lcdfb_info __initdata ek_lcdc_data = {
.default_monspecs = &at91fb_default_stn_monspecs,
.atmel_lcdfb_power_control = at91_lcdc_stn_power_control,
.guard_time = 1,
+#if defined(CONFIG_MACH_AT91SAM9G10EK)
+ .lcd_wiring_mode = ATMEL_LCDC_WIRING_RGB,
+#endif
};
#else
@@ -467,6 +474,9 @@ static struct atmel_lcdfb_info __initdata ek_lcdc_data = {
.default_monspecs = &at91fb_default_tft_monspecs,
.atmel_lcdfb_power_control = at91_lcdc_tft_power_control,
.guard_time = 1,
+#if defined(CONFIG_MACH_AT91SAM9G10EK)
+ .lcd_wiring_mode = ATMEL_LCDC_WIRING_RGB,
+#endif
};
#endif
@@ -600,7 +610,11 @@ static void __init ek_board_init(void)
at91_gpio_leds(ek_leds, ARRAY_SIZE(ek_leds));
}
+#if defined(CONFIG_MACH_AT91SAM9261EK)
MACHINE_START(AT91SAM9261EK, "Atmel AT91SAM9261-EK")
+#else
+MACHINE_START(AT91SAM9G10EK, "Atmel AT91SAM9G10-EK")
+#endif
/* Maintainer: Atmel */
.phys_io = AT91_BASE_SYS,
.io_pg_offst = (AT91_VA_BASE_SYS >> 18) & 0xfffc,
diff --git a/arch/arm/mach-at91/board-sam9263ek.c b/arch/arm/mach-at91/board-sam9263ek.c
index 57d52528f22..1bf7bd4cbe1 100644
--- a/arch/arm/mach-at91/board-sam9263ek.c
+++ b/arch/arm/mach-at91/board-sam9263ek.c
@@ -364,9 +364,9 @@ static void __init ek_add_device_buttons(void) {}
/*
* AC97
+ * reset_pin is not connected: NRST
*/
-static struct atmel_ac97_data ek_ac97_data = {
- .reset_pin = AT91_PIN_PA13,
+static struct ac97c_platform_data ek_ac97_data = {
};
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index a55398ed121..ca470d504ea 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -273,6 +273,7 @@ static void __init ek_add_device_buttons(void) {}
static struct i2c_board_info __initdata ek_i2c_devices[] = {
{
I2C_BOARD_INFO("24c512", 0x50),
+ I2C_BOARD_INFO("wm8731", 0x1b),
},
};
diff --git a/arch/arm/mach-at91/board-sam9m10g45ek.c b/arch/arm/mach-at91/board-sam9m10g45ek.c
new file mode 100644
index 00000000000..b8558eae522
--- /dev/null
+++ b/arch/arm/mach-at91/board-sam9m10g45ek.c
@@ -0,0 +1,389 @@
+/*
+ * Board-specific setup code for the AT91SAM9M10G45 Evaluation Kit family
+ *
+ * Covers: * AT91SAM9G45-EKES board
+ * * AT91SAM9M10G45-EK board
+ *
+ * Copyright (C) 2009 Atmel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/fb.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+#include <linux/clk.h>
+
+#include <mach/hardware.h>
+#include <video/atmel_lcdc.h>
+
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/irq.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/at91sam9_smc.h>
+#include <mach/at91_shdwc.h>
+
+#include "sam9_smc.h"
+#include "generic.h"
+
+
+static void __init ek_map_io(void)
+{
+ /* Initialize processor: 12.000 MHz crystal */
+ at91sam9g45_initialize(12000000);
+
+ /* DGBU on ttyS0. (Rx & Tx only) */
+ at91_register_uart(0, 0, 0);
+
+ /* USART0 not connected on the -EK board */
+ /* USART1 on ttyS2. (Rx, Tx, RTS, CTS) */
+ at91_register_uart(AT91SAM9G45_ID_US1, 2, ATMEL_UART_CTS | ATMEL_UART_RTS);
+
+ /* set serial console to ttyS0 (ie, DBGU) */
+ at91_set_serial_console(0);
+}
+
+static void __init ek_init_irq(void)
+{
+ at91sam9g45_init_interrupts(NULL);
+}
+
+
+/*
+ * USB HS Host port (common to OHCI & EHCI)
+ */
+static struct at91_usbh_data __initdata ek_usbh_hs_data = {
+ .ports = 2,
+ .vbus_pin = {AT91_PIN_PD1, AT91_PIN_PD3},
+};
+
+
+/*
+ * USB HS Device port
+ */
+static struct usba_platform_data __initdata ek_usba_udc_data = {
+ .vbus_pin = AT91_PIN_PB19,
+};
+
+
+/*
+ * SPI devices.
+ */
+static struct spi_board_info ek_spi_devices[] = {
+ { /* DataFlash chip */
+ .modalias = "mtd_dataflash",
+ .chip_select = 0,
+ .max_speed_hz = 15 * 1000 * 1000,
+ .bus_num = 0,
+ },
+};
+
+
+/*
+ * MACB Ethernet device
+ */
+static struct at91_eth_data __initdata ek_macb_data = {
+ .phy_irq_pin = AT91_PIN_PD5,
+ .is_rmii = 1,
+};
+
+
+/*
+ * NAND flash
+ */
+static struct mtd_partition __initdata ek_nand_partition[] = {
+ {
+ .name = "Partition 1",
+ .offset = 0,
+ .size = SZ_64M,
+ },
+ {
+ .name = "Partition 2",
+ .offset = MTDPART_OFS_NXTBLK,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct mtd_partition * __init nand_partitions(int size, int *num_partitions)
+{
+ *num_partitions = ARRAY_SIZE(ek_nand_partition);
+ return ek_nand_partition;
+}
+
+/* det_pin is not connected */
+static struct atmel_nand_data __initdata ek_nand_data = {
+ .ale = 21,
+ .cle = 22,
+ .rdy_pin = AT91_PIN_PC8,
+ .enable_pin = AT91_PIN_PC14,
+ .partition_info = nand_partitions,
+#if defined(CONFIG_MTD_NAND_AT91_BUSWIDTH_16)
+ .bus_width_16 = 1,
+#else
+ .bus_width_16 = 0,
+#endif
+};
+
+static struct sam9_smc_config __initdata ek_nand_smc_config = {
+ .ncs_read_setup = 0,
+ .nrd_setup = 2,
+ .ncs_write_setup = 0,
+ .nwe_setup = 2,
+
+ .ncs_read_pulse = 4,
+ .nrd_pulse = 4,
+ .ncs_write_pulse = 4,
+ .nwe_pulse = 4,
+
+ .read_cycle = 7,
+ .write_cycle = 7,
+
+ .mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE,
+ .tdf_cycles = 3,
+};
+
+static void __init ek_add_device_nand(void)
+{
+ /* setup bus-width (8 or 16) */
+ if (ek_nand_data.bus_width_16)
+ ek_nand_smc_config.mode |= AT91_SMC_DBW_16;
+ else
+ ek_nand_smc_config.mode |= AT91_SMC_DBW_8;
+
+ /* configure chip-select 3 (NAND) */
+ sam9_smc_configure(3, &ek_nand_smc_config);
+
+ at91_add_device_nand(&ek_nand_data);
+}
+
+
+/*
+ * LCD Controller
+ */
+#if defined(CONFIG_FB_ATMEL) || defined(CONFIG_FB_ATMEL_MODULE)
+static struct fb_videomode at91_tft_vga_modes[] = {
+ {
+ .name = "LG",
+ .refresh = 60,
+ .xres = 480, .yres = 272,
+ .pixclock = KHZ2PICOS(9000),
+
+ .left_margin = 1, .right_margin = 1,
+ .upper_margin = 40, .lower_margin = 1,
+ .hsync_len = 45, .vsync_len = 1,
+
+ .sync = 0,
+ .vmode = FB_VMODE_NONINTERLACED,
+ },
+};
+
+static struct fb_monspecs at91fb_default_monspecs = {
+ .manufacturer = "LG",
+ .monitor = "LB043WQ1",
+
+ .modedb = at91_tft_vga_modes,
+ .modedb_len = ARRAY_SIZE(at91_tft_vga_modes),
+ .hfmin = 15000,
+ .hfmax = 17640,
+ .vfmin = 57,
+ .vfmax = 67,
+};
+
+#define AT91SAM9G45_DEFAULT_LCDCON2 (ATMEL_LCDC_MEMOR_LITTLE \
+ | ATMEL_LCDC_DISTYPE_TFT \
+ | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE)
+
+/* Driver datas */
+static struct atmel_lcdfb_info __initdata ek_lcdc_data = {
+ .lcdcon_is_backlight = true,
+ .default_bpp = 32,
+ .default_dmacon = ATMEL_LCDC_DMAEN,
+ .default_lcdcon2 = AT91SAM9G45_DEFAULT_LCDCON2,
+ .default_monspecs = &at91fb_default_monspecs,
+ .guard_time = 9,
+ .lcd_wiring_mode = ATMEL_LCDC_WIRING_RGB,
+};
+
+#else
+static struct atmel_lcdfb_info __initdata ek_lcdc_data;
+#endif
+
+
+/*
+ * GPIO Buttons
+ */
+#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
+static struct gpio_keys_button ek_buttons[] = {
+ { /* BP1, "leftclic" */
+ .code = BTN_LEFT,
+ .gpio = AT91_PIN_PB6,
+ .active_low = 1,
+ .desc = "left_click",
+ .wakeup = 1,
+ },
+ { /* BP2, "rightclic" */
+ .code = BTN_RIGHT,
+ .gpio = AT91_PIN_PB7,
+ .active_low = 1,
+ .desc = "right_click",
+ .wakeup = 1,
+ },
+ /* BP3, "joystick" */
+ {
+ .code = KEY_LEFT,
+ .gpio = AT91_PIN_PB14,
+ .active_low = 1,
+ .desc = "Joystick Left",
+ },
+ {
+ .code = KEY_RIGHT,
+ .gpio = AT91_PIN_PB15,
+ .active_low = 1,
+ .desc = "Joystick Right",
+ },
+ {
+ .code = KEY_UP,
+ .gpio = AT91_PIN_PB16,
+ .active_low = 1,
+ .desc = "Joystick Up",
+ },
+ {
+ .code = KEY_DOWN,
+ .gpio = AT91_PIN_PB17,
+ .active_low = 1,
+ .desc = "Joystick Down",
+ },
+ {
+ .code = KEY_ENTER,
+ .gpio = AT91_PIN_PB18,
+ .active_low = 1,
+ .desc = "Joystick Press",
+ },
+};
+
+static struct gpio_keys_platform_data ek_button_data = {
+ .buttons = ek_buttons,
+ .nbuttons = ARRAY_SIZE(ek_buttons),
+};
+
+static struct platform_device ek_button_device = {
+ .name = "gpio-keys",
+ .id = -1,
+ .num_resources = 0,
+ .dev = {
+ .platform_data = &ek_button_data,
+ }
+};
+
+static void __init ek_add_device_buttons(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ek_buttons); i++) {
+ at91_set_GPIO_periph(ek_buttons[i].gpio, 1);
+ at91_set_deglitch(ek_buttons[i].gpio, 1);
+ }
+
+ platform_device_register(&ek_button_device);
+}
+#else
+static void __init ek_add_device_buttons(void) {}
+#endif
+
+
+/*
+ * LEDs ... these could all be PWM-driven, for variable brightness
+ */
+static struct gpio_led ek_leds[] = {
+ { /* "top" led, red, powerled */
+ .name = "d8",
+ .gpio = AT91_PIN_PD30,
+ .default_trigger = "heartbeat",
+ },
+ { /* "left" led, green, userled2, pwm3 */
+ .name = "d6",
+ .gpio = AT91_PIN_PD0,
+ .active_low = 1,
+ .default_trigger = "nand-disk",
+ },
+#if !(defined(CONFIG_LEDS_ATMEL_PWM) || defined(CONFIG_LEDS_ATMEL_PWM_MODULE))
+ { /* "right" led, green, userled1, pwm1 */
+ .name = "d7",
+ .gpio = AT91_PIN_PD31,
+ .active_low = 1,
+ .default_trigger = "mmc0",
+ },
+#endif
+};
+
+
+/*
+ * PWM Leds
+ */
+static struct gpio_led ek_pwm_led[] = {
+#if defined(CONFIG_LEDS_ATMEL_PWM) || defined(CONFIG_LEDS_ATMEL_PWM_MODULE)
+ { /* "right" led, green, userled1, pwm1 */
+ .name = "d7",
+ .gpio = 1, /* is PWM channel number */
+ .active_low = 1,
+ .default_trigger = "none",
+ },
+#endif
+};
+
+
+
+static void __init ek_board_init(void)
+{
+ /* Serial */
+ at91_add_device_serial();
+ /* USB HS Host */
+ at91_add_device_usbh_ohci(&ek_usbh_hs_data);
+ /* USB HS Device */
+ at91_add_device_usba(&ek_usba_udc_data);
+ /* SPI */
+ at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));
+ /* Ethernet */
+ at91_add_device_eth(&ek_macb_data);
+ /* NAND */
+ ek_add_device_nand();
+ /* I2C */
+ at91_add_device_i2c(0, NULL, 0);
+ /* LCD Controller */
+ at91_add_device_lcdc(&ek_lcdc_data);
+ /* Push Buttons */
+ ek_add_device_buttons();
+ /* LEDs */
+ at91_gpio_leds(ek_leds, ARRAY_SIZE(ek_leds));
+ at91_pwm_leds(ek_pwm_led, ARRAY_SIZE(ek_pwm_led));
+}
+
+MACHINE_START(AT91SAM9G45EKES, "Atmel AT91SAM9G45-EKES")
+ /* Maintainer: Atmel */
+ .phys_io = AT91_BASE_SYS,
+ .io_pg_offst = (AT91_VA_BASE_SYS >> 18) & 0xfffc,
+ .boot_params = AT91_SDRAM_BASE + 0x100,
+ .timer = &at91sam926x_timer,
+ .map_io = ek_map_io,
+ .init_irq = ek_init_irq,
+ .init_machine = ek_board_init,
+MACHINE_END
diff --git a/arch/arm/mach-at91/board-sam9rlek.c b/arch/arm/mach-at91/board-sam9rlek.c
index f6b5672cabd..9d07679efce 100644
--- a/arch/arm/mach-at91/board-sam9rlek.c
+++ b/arch/arm/mach-at91/board-sam9rlek.c
@@ -15,6 +15,8 @@
#include <linux/spi/spi.h>
#include <linux/fb.h>
#include <linux/clk.h>
+#include <linux/input.h>
+#include <linux/gpio_keys.h>
#include <video/atmel_lcdc.h>
@@ -208,6 +210,79 @@ static struct atmel_lcdfb_info __initdata ek_lcdc_data;
#endif
+/*
+ * LEDs
+ */
+static struct gpio_led ek_leds[] = {
+ { /* "bottom" led, green, userled1 to be defined */
+ .name = "ds1",
+ .gpio = AT91_PIN_PD15,
+ .active_low = 1,
+ .default_trigger = "none",
+ },
+ { /* "bottom" led, green, userled2 to be defined */
+ .name = "ds2",
+ .gpio = AT91_PIN_PD16,
+ .active_low = 1,
+ .default_trigger = "none",
+ },
+ { /* "power" led, yellow */
+ .name = "ds3",
+ .gpio = AT91_PIN_PD14,
+ .default_trigger = "heartbeat",
+ }
+};
+
+
+/*
+ * GPIO Buttons
+ */
+#if defined(CONFIG_KEYBOARD_GPIO) || defined(CONFIG_KEYBOARD_GPIO_MODULE)
+static struct gpio_keys_button ek_buttons[] = {
+ {
+ .gpio = AT91_PIN_PB0,
+ .code = BTN_2,
+ .desc = "Right Click",
+ .active_low = 1,
+ .wakeup = 1,
+ },
+ {
+ .gpio = AT91_PIN_PB1,
+ .code = BTN_1,
+ .desc = "Left Click",
+ .active_low = 1,
+ .wakeup = 1,
+ }
+};
+
+static struct gpio_keys_platform_data ek_button_data = {
+ .buttons = ek_buttons,
+ .nbuttons = ARRAY_SIZE(ek_buttons),
+};
+
+static struct platform_device ek_button_device = {
+ .name = "gpio-keys",
+ .id = -1,
+ .num_resources = 0,
+ .dev = {
+ .platform_data = &ek_button_data,
+ }
+};
+
+static void __init ek_add_device_buttons(void)
+{
+ at91_set_gpio_input(AT91_PIN_PB1, 1); /* btn1 */
+ at91_set_deglitch(AT91_PIN_PB1, 1);
+ at91_set_gpio_input(AT91_PIN_PB0, 1); /* btn2 */
+ at91_set_deglitch(AT91_PIN_PB0, 1);
+
+ platform_device_register(&ek_button_device);
+}
+#else
+static void __init ek_add_device_buttons(void) {}
+#endif
+
+
static void __init ek_board_init(void)
{
/* Serial */
@@ -226,6 +301,10 @@ static void __init ek_board_init(void)
at91_add_device_lcdc(&ek_lcdc_data);
/* Touch Screen Controller */
at91_add_device_tsadcc();
+ /* LEDs */
+ at91_gpio_leds(ek_leds, ARRAY_SIZE(ek_leds));
+ /* Push Buttons */
+ ek_add_device_buttons();
}
MACHINE_START(AT91SAM9RLEK, "Atmel AT91SAM9RL-EK")
diff --git a/arch/arm/mach-at91/clock.c b/arch/arm/mach-at91/clock.c
index bac578fe0d3..c042dcf4725 100644
--- a/arch/arm/mach-at91/clock.c
+++ b/arch/arm/mach-at91/clock.c
@@ -47,20 +47,25 @@
* Chips have some kind of clocks : group them by functionality
*/
#define cpu_has_utmi() ( cpu_is_at91cap9() \
- || cpu_is_at91sam9rl())
+ || cpu_is_at91sam9rl() \
+ || cpu_is_at91sam9g45())
-#define cpu_has_800M_plla() (cpu_is_at91sam9g20())
+#define cpu_has_800M_plla() ( cpu_is_at91sam9g20() \
+ || cpu_is_at91sam9g45())
-#define cpu_has_pllb() (!cpu_is_at91sam9rl())
+#define cpu_has_300M_plla() (cpu_is_at91sam9g10())
-#define cpu_has_upll() (0)
+#define cpu_has_pllb() (!(cpu_is_at91sam9rl() \
+ || cpu_is_at91sam9g45()))
+
+#define cpu_has_upll() (cpu_is_at91sam9g45())
/* USB host HS & FS */
#define cpu_has_uhp() (!cpu_is_at91sam9rl())
/* USB device FS only */
-#define cpu_has_udpfs() (!cpu_is_at91sam9rl())
-
+#define cpu_has_udpfs() (!(cpu_is_at91sam9rl() \
+ || cpu_is_at91sam9g45()))
static LIST_HEAD(clocks);
static DEFINE_SPINLOCK(clk_lock);
@@ -133,6 +138,13 @@ static void pmc_uckr_mode(struct clk *clk, int is_on)
{
unsigned int uckr = at91_sys_read(AT91_CKGR_UCKR);
+ if (cpu_is_at91sam9g45()) {
+ if (is_on)
+ uckr |= AT91_PMC_BIASEN;
+ else
+ uckr &= ~AT91_PMC_BIASEN;
+ }
+
if (is_on) {
is_on = AT91_PMC_LOCKU;
at91_sys_write(AT91_CKGR_UCKR, uckr | clk->pmc_mask);
@@ -310,6 +322,7 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
unsigned long flags;
unsigned prescale;
unsigned long actual;
+ unsigned long prev = ULONG_MAX;
if (!clk_is_programmable(clk))
return -EINVAL;
@@ -317,8 +330,16 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
actual = clk->parent->rate_hz;
for (prescale = 0; prescale < 7; prescale++) {
- if (actual && actual <= rate)
+ if (actual > rate)
+ prev = actual;
+
+ if (actual && actual <= rate) {
+ if ((prev - rate) < (rate - actual)) {
+ actual = prev;
+ prescale--;
+ }
break;
+ }
actual >>= 1;
}
@@ -373,6 +394,10 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
return -EBUSY;
if (!clk_is_primary(parent) || !clk_is_programmable(clk))
return -EINVAL;
+
+ if (cpu_is_at91sam9rl() && parent->id == AT91_PMC_CSS_PLLB)
+ return -EINVAL;
+
spin_lock_irqsave(&clk_lock, flags);
clk->rate_hz = parent->rate_hz;
@@ -601,7 +626,9 @@ static void __init at91_pllb_usbfs_clock_init(unsigned long main_clock)
uhpck.pmc_mask = AT91RM9200_PMC_UHP;
udpck.pmc_mask = AT91RM9200_PMC_UDP;
at91_sys_write(AT91_PMC_SCER, AT91RM9200_PMC_MCKUDP);
- } else if (cpu_is_at91sam9260() || cpu_is_at91sam9261() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) {
+ } else if (cpu_is_at91sam9260() || cpu_is_at91sam9261() ||
+ cpu_is_at91sam9263() || cpu_is_at91sam9g20() ||
+ cpu_is_at91sam9g10()) {
uhpck.pmc_mask = AT91SAM926x_PMC_UHP;
udpck.pmc_mask = AT91SAM926x_PMC_UDP;
} else if (cpu_is_at91cap9()) {
@@ -637,6 +664,7 @@ int __init at91_clock_init(unsigned long main_clock)
{
unsigned tmp, freq, mckr;
int i;
+ int pll_overclock = false;
/*
* When the bootloader initialized the main oscillator correctly,
@@ -654,12 +682,25 @@ int __init at91_clock_init(unsigned long main_clock)
/* report if PLLA is more than mildly overclocked */
plla.rate_hz = at91_pll_rate(&plla, main_clock, at91_sys_read(AT91_CKGR_PLLAR));
- if ((!cpu_has_800M_plla() && plla.rate_hz > 209000000)
- || (cpu_has_800M_plla() && plla.rate_hz > 800000000))
+ if (cpu_has_300M_plla()) {
+ if (plla.rate_hz > 300000000)
+ pll_overclock = true;
+ } else if (cpu_has_800M_plla()) {
+ if (plla.rate_hz > 800000000)
+ pll_overclock = true;
+ } else {
+ if (plla.rate_hz > 209000000)
+ pll_overclock = true;
+ }
+ if (pll_overclock)
pr_info("Clocks: PLLA overclocked, %ld MHz\n", plla.rate_hz / 1000000);
+ if (cpu_is_at91sam9g45()) {
+ mckr = at91_sys_read(AT91_PMC_MCKR);
+ plla.rate_hz /= (1 << ((mckr & AT91_PMC_PLLADIV2) >> 12)); /* plla divisor by 2 */
+ }
- if (cpu_has_upll() && !cpu_has_pllb()) {
+ if (!cpu_has_pllb() && cpu_has_upll()) {
/* setup UTMI clock as the fourth primary clock
* (instead of pllb) */
utmi_clk.type |= CLK_TYPE_PRIMARY;
@@ -701,6 +742,9 @@ int __init at91_clock_init(unsigned long main_clock)
freq / ((mckr & AT91_PMC_MDIV) >> 7) : freq; /* mdiv ; (x >> 7) = ((x >> 8) * 2) */
if (mckr & AT91_PMC_PDIV)
freq /= 2; /* processor clock division */
+ } else if (cpu_is_at91sam9g45()) {
+ mck.rate_hz = (mckr & AT91_PMC_MDIV) == AT91SAM9_PMC_MDIV_3 ?
+ freq / 3 : freq / (1 << ((mckr & AT91_PMC_MDIV) >> 8)); /* mdiv */
} else {
mck.rate_hz = freq / (1 << ((mckr & AT91_PMC_MDIV) >> 8)); /* mdiv */
}
diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h
index b5daf7f5e01..88e413b3848 100644
--- a/arch/arm/mach-at91/generic.h
+++ b/arch/arm/mach-at91/generic.h
@@ -14,6 +14,7 @@ extern void __init at91sam9260_initialize(unsigned long main_clock);
extern void __init at91sam9261_initialize(unsigned long main_clock);
extern void __init at91sam9263_initialize(unsigned long main_clock);
extern void __init at91sam9rl_initialize(unsigned long main_clock);
+extern void __init at91sam9g45_initialize(unsigned long main_clock);
extern void __init at91x40_initialize(unsigned long main_clock);
extern void __init at91cap9_initialize(unsigned long main_clock);
@@ -23,6 +24,7 @@ extern void __init at91sam9260_init_interrupts(unsigned int priority[]);
extern void __init at91sam9261_init_interrupts(unsigned int priority[]);
extern void __init at91sam9263_init_interrupts(unsigned int priority[]);
extern void __init at91sam9rl_init_interrupts(unsigned int priority[]);
+extern void __init at91sam9g45_init_interrupts(unsigned int priority[]);
extern void __init at91x40_init_interrupts(unsigned int priority[]);
extern void __init at91cap9_init_interrupts(unsigned int priority[]);
extern void __init at91_aic_init(unsigned int priority[]);
diff --git a/arch/arm/mach-at91/gpio.c b/arch/arm/mach-at91/gpio.c
index f2236f0e101..ae4772e744a 100644
--- a/arch/arm/mach-at91/gpio.c
+++ b/arch/arm/mach-at91/gpio.c
@@ -44,13 +44,11 @@ static int at91_gpiolib_direction_output(struct gpio_chip *chip,
unsigned offset, int val);
static int at91_gpiolib_direction_input(struct gpio_chip *chip,
unsigned offset);
-static int at91_gpiolib_request(struct gpio_chip *chip, unsigned offset);
#define AT91_GPIO_CHIP(name, base_gpio, nr_gpio) \
{ \
.chip = { \
.label = name, \
- .request = at91_gpiolib_request, \
.direction_input = at91_gpiolib_direction_input, \
.direction_output = at91_gpiolib_direction_output, \
.get = at91_gpiolib_get, \
@@ -588,19 +586,6 @@ static void at91_gpiolib_set(struct gpio_chip *chip, unsigned offset, int val)
__raw_writel(mask, pio + (val ? PIO_SODR : PIO_CODR));
}
-static int at91_gpiolib_request(struct gpio_chip *chip, unsigned offset)
-{
- unsigned pin = chip->base + offset;
- void __iomem *pio = pin_to_controller(pin);
- unsigned mask = pin_to_mask(pin);
-
- /* Cannot request GPIOs that are in alternate function mode */
- if (!(__raw_readl(pio + PIO_PSR) & mask))
- return -EPERM;
-
- return 0;
-}
-
static void at91_gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
int i;
diff --git a/arch/arm/mach-at91/include/mach/at91sam9261.h b/arch/arm/mach-at91/include/mach/at91sam9261.h
index 3a348ca2077..87de8be1748 100644
--- a/arch/arm/mach-at91/include/mach/at91sam9261.h
+++ b/arch/arm/mach-at91/include/mach/at91sam9261.h
@@ -95,6 +95,9 @@
#define AT91SAM9261_SRAM_BASE 0x00300000 /* Internal SRAM base address */
#define AT91SAM9261_SRAM_SIZE 0x00028000 /* Internal SRAM size (160Kb) */
+#define AT91SAM9G10_SRAM_BASE AT91SAM9261_SRAM_BASE /* Internal SRAM base address */
+#define AT91SAM9G10_SRAM_SIZE 0x00004000 /* Internal SRAM size (16Kb) */
+
#define AT91SAM9261_ROM_BASE 0x00400000 /* Internal ROM base address */
#define AT91SAM9261_ROM_SIZE SZ_32K /* Internal ROM size (32Kb) */
diff --git a/arch/arm/mach-at91/include/mach/at91sam9g45.h b/arch/arm/mach-at91/include/mach/at91sam9g45.h
new file mode 100644
index 00000000000..a526869aee3
--- /dev/null
+++ b/arch/arm/mach-at91/include/mach/at91sam9g45.h
@@ -0,0 +1,155 @@
+/*
+ * Chip-specific header file for the AT91SAM9G45 family
+ *
+ * Copyright (C) 2008-2009 Atmel Corporation.
+ *
+ * Common definitions.
+ * Based on AT91SAM9G45 preliminary datasheet.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef AT91SAM9G45_H
+#define AT91SAM9G45_H
+
+/*
+ * Peripheral identifiers/interrupts.
+ */
+#define AT91_ID_FIQ 0 /* Advanced Interrupt Controller (FIQ) */
+#define AT91_ID_SYS 1 /* System Controller Interrupt */
+#define AT91SAM9G45_ID_PIOA 2 /* Parallel I/O Controller A */
+#define AT91SAM9G45_ID_PIOB 3 /* Parallel I/O Controller B */
+#define AT91SAM9G45_ID_PIOC 4 /* Parallel I/O Controller C */
+#define AT91SAM9G45_ID_PIODE 5 /* Parallel I/O Controller D and E */
+#define AT91SAM9G45_ID_TRNG 6 /* True Random Number Generator */
+#define AT91SAM9G45_ID_US0 7 /* USART 0 */
+#define AT91SAM9G45_ID_US1 8 /* USART 1 */
+#define AT91SAM9G45_ID_US2 9 /* USART 2 */
+#define AT91SAM9G45_ID_US3 10 /* USART 3 */
+#define AT91SAM9G45_ID_MCI0 11 /* High Speed Multimedia Card Interface 0 */
+#define AT91SAM9G45_ID_TWI0 12 /* Two-Wire Interface 0 */
+#define AT91SAM9G45_ID_TWI1 13 /* Two-Wire Interface 1 */
+#define AT91SAM9G45_ID_SPI0 14 /* Serial Peripheral Interface 0 */
+#define AT91SAM9G45_ID_SPI1 15 /* Serial Peripheral Interface 1 */
+#define AT91SAM9G45_ID_SSC0 16 /* Synchronous Serial Controller 0 */
+#define AT91SAM9G45_ID_SSC1 17 /* Synchronous Serial Controller 1 */
+#define AT91SAM9G45_ID_TCB 18 /* Timer Counter 0, 1, 2, 3, 4 and 5 */
+#define AT91SAM9G45_ID_PWMC 19 /* Pulse Width Modulation Controller */
+#define AT91SAM9G45_ID_TSC 20 /* Touch Screen ADC Controller */
+#define AT91SAM9G45_ID_DMA 21 /* DMA Controller */
+#define AT91SAM9G45_ID_UHPHS 22 /* USB Host High Speed */
+#define AT91SAM9G45_ID_LCDC 23 /* LCD Controller */
+#define AT91SAM9G45_ID_AC97C 24 /* AC97 Controller */
+#define AT91SAM9G45_ID_EMAC 25 /* Ethernet MAC */
+#define AT91SAM9G45_ID_ISI 26 /* Image Sensor Interface */
+#define AT91SAM9G45_ID_UDPHS 27 /* USB Device High Speed */
+#define AT91SAM9G45_ID_AESTDESSHA 28 /* AES + T-DES + SHA */
+#define AT91SAM9G45_ID_MCI1 29 /* High Speed Multimedia Card Interface 1 */
+#define AT91SAM9G45_ID_VDEC 30 /* Video Decoder */
+#define AT91SAM9G45_ID_IRQ0 31 /* Advanced Interrupt Controller */
+
+/*
+ * User Peripheral physical base addresses.
+ */
+#define AT91SAM9G45_BASE_UDPHS 0xfff78000
+#define AT91SAM9G45_BASE_TCB0 0xfff7c000
+#define AT91SAM9G45_BASE_TC0 0xfff7c000
+#define AT91SAM9G45_BASE_TC1 0xfff7c040
+#define AT91SAM9G45_BASE_TC2 0xfff7c080
+#define AT91SAM9G45_BASE_MCI0 0xfff80000
+#define AT91SAM9G45_BASE_TWI0 0xfff84000
+#define AT91SAM9G45_BASE_TWI1 0xfff88000
+#define AT91SAM9G45_BASE_US0 0xfff8c000
+#define AT91SAM9G45_BASE_US1 0xfff90000
+#define AT91SAM9G45_BASE_US2 0xfff94000
+#define AT91SAM9G45_BASE_US3 0xfff98000
+#define AT91SAM9G45_BASE_SSC0 0xfff9c000
+#define AT91SAM9G45_BASE_SSC1 0xfffa0000
+#define AT91SAM9G45_BASE_SPI0 0xfffa4000
+#define AT91SAM9G45_BASE_SPI1 0xfffa8000
+#define AT91SAM9G45_BASE_AC97C 0xfffac000
+#define AT91SAM9G45_BASE_TSC 0xfffb0000
+#define AT91SAM9G45_BASE_ISI 0xfffb4000
+#define AT91SAM9G45_BASE_PWMC 0xfffb8000
+#define AT91SAM9G45_BASE_EMAC 0xfffbc000
+#define AT91SAM9G45_BASE_AES 0xfffc0000
+#define AT91SAM9G45_BASE_TDES 0xfffc4000
+#define AT91SAM9G45_BASE_SHA 0xfffc8000
+#define AT91SAM9G45_BASE_TRNG 0xfffcc000
+#define AT91SAM9G45_BASE_MCI1 0xfffd0000
+#define AT91SAM9G45_BASE_TCB1 0xfffd4000
+#define AT91SAM9G45_BASE_TC3 0xfffd4000
+#define AT91SAM9G45_BASE_TC4 0xfffd4040
+#define AT91SAM9G45_BASE_TC5 0xfffd4080
+#define AT91_BASE_SYS 0xffffe200
+
+/*
+ * System Peripherals (offset from AT91_BASE_SYS)
+ */
+#define AT91_ECC (0xffffe200 - AT91_BASE_SYS)
+#define AT91_DDRSDRC1 (0xffffe400 - AT91_BASE_SYS)
+#define AT91_DDRSDRC0 (0xffffe600 - AT91_BASE_SYS)
+#define AT91_SMC (0xffffe800 - AT91_BASE_SYS)
+#define AT91_MATRIX (0xffffea00 - AT91_BASE_SYS)
+#define AT91_DMA (0xffffec00 - AT91_BASE_SYS)
+#define AT91_DBGU (0xffffee00 - AT91_BASE_SYS)
+#define AT91_AIC (0xfffff000 - AT91_BASE_SYS)
+#define AT91_PIOA (0xfffff200 - AT91_BASE_SYS)
+#define AT91_PIOB (0xfffff400 - AT91_BASE_SYS)
+#define AT91_PIOC (0xfffff600 - AT91_BASE_SYS)
+#define AT91_PIOD (0xfffff800 - AT91_BASE_SYS)
+#define AT91_PIOE (0xfffffa00 - AT91_BASE_SYS)
+#define AT91_PMC (0xfffffc00 - AT91_BASE_SYS)
+#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS)
+#define AT91_SHDWC (0xfffffd10 - AT91_BASE_SYS)
+#define AT91_RTT (0xfffffd20 - AT91_BASE_SYS)
+#define AT91_PIT (0xfffffd30 - AT91_BASE_SYS)
+#define AT91_WDT (0xfffffd40 - AT91_BASE_SYS)
+#define AT91_GPBR (0xfffffd60 - AT91_BASE_SYS)
+#define AT91_RTC (0xfffffdb0 - AT91_BASE_SYS)
+
+#define AT91_USART0 AT91SAM9G45_BASE_US0
+#define AT91_USART1 AT91SAM9G45_BASE_US1
+#define AT91_USART2 AT91SAM9G45_BASE_US2
+#define AT91_USART3 AT91SAM9G45_BASE_US3
+
+/*
+ * Internal Memory.
+ */
+#define AT91SAM9G45_SRAM_BASE 0x00300000 /* Internal SRAM base address */
+#define AT91SAM9G45_SRAM_SIZE SZ_64K /* Internal SRAM size (64Kb) */
+
+#define AT91SAM9G45_ROM_BASE 0x00400000 /* Internal ROM base address */
+#define AT91SAM9G45_ROM_SIZE SZ_64K /* Internal ROM size (64Kb) */
+
+#define AT91SAM9G45_LCDC_BASE 0x00500000 /* LCD Controller */
+#define AT91SAM9G45_UDPHS_FIFO 0x00600000 /* USB Device HS controller */
+#define AT91SAM9G45_OHCI_BASE 0x00700000 /* USB Host controller (OHCI) */
+#define AT91SAM9G45_EHCI_BASE 0x00800000 /* USB Host controller (EHCI) */
+#define AT91SAM9G45_VDEC_BASE 0x00900000 /* Video Decoder Controller */
+
+#define CONFIG_DRAM_BASE AT91_CHIPSELECT_6
+
+#define CONSISTENT_DMA_SIZE SZ_4M
+
+/*
+ * DMA peripheral identifiers
+ * for hardware handshaking interface
+ */
+#define AT_DMA_ID_MCI0 0
+#define AT_DMA_ID_SPI0_TX 1
+#define AT_DMA_ID_SPI0_RX 2
+#define AT_DMA_ID_SPI1_TX 3
+#define AT_DMA_ID_SPI1_RX 4
+#define AT_DMA_ID_SSC0_TX 5
+#define AT_DMA_ID_SSC0_RX 6
+#define AT_DMA_ID_SSC1_TX 7
+#define AT_DMA_ID_SSC1_RX 8
+#define AT_DMA_ID_AC97_TX 9
+#define AT_DMA_ID_AC97_RX 10
+#define AT_DMA_ID_MCI1 13
+
+#endif
diff --git a/arch/arm/mach-at91/include/mach/at91sam9g45_matrix.h b/arch/arm/mach-at91/include/mach/at91sam9g45_matrix.h
new file mode 100644
index 00000000000..c972d60e0ae
--- /dev/null
+++ b/arch/arm/mach-at91/include/mach/at91sam9g45_matrix.h
@@ -0,0 +1,153 @@
+/*
+ * Matrix-centric header file for the AT91SAM9G45 family
+ *
+ * Copyright (C) 2008-2009 Atmel Corporation.
+ *
+ * Memory Controllers (MATRIX, EBI) - System peripherals registers.
+ * Based on AT91SAM9G45 preliminary datasheet.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef AT91SAM9G45_MATRIX_H
+#define AT91SAM9G45_MATRIX_H
+
+#define AT91_MATRIX_MCFG0 (AT91_MATRIX + 0x00) /* Master Configuration Register 0 */
+#define AT91_MATRIX_MCFG1 (AT91_MATRIX + 0x04) /* Master Configuration Register 1 */
+#define AT91_MATRIX_MCFG2 (AT91_MATRIX + 0x08) /* Master Configuration Register 2 */
+#define AT91_MATRIX_MCFG3 (AT91_MATRIX + 0x0C) /* Master Configuration Register 3 */
+#define AT91_MATRIX_MCFG4 (AT91_MATRIX + 0x10) /* Master Configuration Register 4 */
+#define AT91_MATRIX_MCFG5 (AT91_MATRIX + 0x14) /* Master Configuration Register 5 */
+#define AT91_MATRIX_MCFG6 (AT91_MATRIX + 0x18) /* Master Configuration Register 6 */
+#define AT91_MATRIX_MCFG7 (AT91_MATRIX + 0x1C) /* Master Configuration Register 7 */
+#define AT91_MATRIX_MCFG8 (AT91_MATRIX + 0x20) /* Master Configuration Register 8 */
+#define AT91_MATRIX_MCFG9 (AT91_MATRIX + 0x24) /* Master Configuration Register 9 */
+#define AT91_MATRIX_MCFG10 (AT91_MATRIX + 0x28) /* Master Configuration Register 10 */
+#define AT91_MATRIX_MCFG11 (AT91_MATRIX + 0x2C) /* Master Configuration Register 11 */
+#define AT91_MATRIX_ULBT (7 << 0) /* Undefined Length Burst Type */
+#define AT91_MATRIX_ULBT_INFINITE (0 << 0)
+#define AT91_MATRIX_ULBT_SINGLE (1 << 0)
+#define AT91_MATRIX_ULBT_FOUR (2 << 0)
+#define AT91_MATRIX_ULBT_EIGHT (3 << 0)
+#define AT91_MATRIX_ULBT_SIXTEEN (4 << 0)
+#define AT91_MATRIX_ULBT_THIRTYTWO (5 << 0)
+#define AT91_MATRIX_ULBT_SIXTYFOUR (6 << 0)
+#define AT91_MATRIX_ULBT_128 (7 << 0)
+
+#define AT91_MATRIX_SCFG0 (AT91_MATRIX + 0x40) /* Slave Configuration Register 0 */
+#define AT91_MATRIX_SCFG1 (AT91_MATRIX + 0x44) /* Slave Configuration Register 1 */
+#define AT91_MATRIX_SCFG2 (AT91_MATRIX + 0x48) /* Slave Configuration Register 2 */
+#define AT91_MATRIX_SCFG3 (AT91_MATRIX + 0x4C) /* Slave Configuration Register 3 */
+#define AT91_MATRIX_SCFG4 (AT91_MATRIX + 0x50) /* Slave Configuration Register 4 */
+#define AT91_MATRIX_SCFG5 (AT91_MATRIX + 0x54) /* Slave Configuration Register 5 */
+#define AT91_MATRIX_SCFG6 (AT91_MATRIX + 0x58) /* Slave Configuration Register 6 */
+#define AT91_MATRIX_SCFG7 (AT91_MATRIX + 0x5C) /* Slave Configuration Register 7 */
+#define AT91_MATRIX_SLOT_CYCLE (0x1ff << 0) /* Maximum Number of Allowed Cycles for a Burst */
+#define AT91_MATRIX_DEFMSTR_TYPE (3 << 16) /* Default Master Type */
+#define AT91_MATRIX_DEFMSTR_TYPE_NONE (0 << 16)
+#define AT91_MATRIX_DEFMSTR_TYPE_LAST (1 << 16)
+#define AT91_MATRIX_DEFMSTR_TYPE_FIXED (2 << 16)
+#define AT91_MATRIX_FIXED_DEFMSTR (0xf << 18) /* Fixed Index of Default Master */
+
+#define AT91_MATRIX_PRAS0 (AT91_MATRIX + 0x80) /* Priority Register A for Slave 0 */
+#define AT91_MATRIX_PRBS0 (AT91_MATRIX + 0x84) /* Priority Register B for Slave 0 */
+#define AT91_MATRIX_PRAS1 (AT91_MATRIX + 0x88) /* Priority Register A for Slave 1 */
+#define AT91_MATRIX_PRBS1 (AT91_MATRIX + 0x8C) /* Priority Register B for Slave 1 */
+#define AT91_MATRIX_PRAS2 (AT91_MATRIX + 0x90) /* Priority Register A for Slave 2 */
+#define AT91_MATRIX_PRBS2 (AT91_MATRIX + 0x94) /* Priority Register B for Slave 2 */
+#define AT91_MATRIX_PRAS3 (AT91_MATRIX + 0x98) /* Priority Register A for Slave 3 */
+#define AT91_MATRIX_PRBS3 (AT91_MATRIX + 0x9C) /* Priority Register B for Slave 3 */
+#define AT91_MATRIX_PRAS4 (AT91_MATRIX + 0xA0) /* Priority Register A for Slave 4 */
+#define AT91_MATRIX_PRBS4 (AT91_MATRIX + 0xA4) /* Priority Register B for Slave 4 */
+#define AT91_MATRIX_PRAS5 (AT91_MATRIX + 0xA8) /* Priority Register A for Slave 5 */
+#define AT91_MATRIX_PRBS5 (AT91_MATRIX + 0xAC) /* Priority Register B for Slave 5 */
+#define AT91_MATRIX_PRAS6 (AT91_MATRIX + 0xB0) /* Priority Register A for Slave 6 */
+#define AT91_MATRIX_PRBS6 (AT91_MATRIX + 0xB4) /* Priority Register B for Slave 6 */
+#define AT91_MATRIX_PRAS7 (AT91_MATRIX + 0xB8) /* Priority Register A for Slave 7 */
+#define AT91_MATRIX_PRBS7 (AT91_MATRIX + 0xBC) /* Priority Register B for Slave 7 */
+#define AT91_MATRIX_M0PR (3 << 0) /* Master 0 Priority */
+#define AT91_MATRIX_M1PR (3 << 4) /* Master 1 Priority */
+#define AT91_MATRIX_M2PR (3 << 8) /* Master 2 Priority */
+#define AT91_MATRIX_M3PR (3 << 12) /* Master 3 Priority */
+#define AT91_MATRIX_M4PR (3 << 16) /* Master 4 Priority */
+#define AT91_MATRIX_M5PR (3 << 20) /* Master 5 Priority */
+#define AT91_MATRIX_M6PR (3 << 24) /* Master 6 Priority */
+#define AT91_MATRIX_M7PR (3 << 28) /* Master 7 Priority */
+#define AT91_MATRIX_M8PR (3 << 0) /* Master 8 Priority (in Register B) */
+#define AT91_MATRIX_M9PR (3 << 4) /* Master 9 Priority (in Register B) */
+#define AT91_MATRIX_M10PR (3 << 8) /* Master 10 Priority (in Register B) */
+#define AT91_MATRIX_M11PR (3 << 12) /* Master 11 Priority (in Register B) */
+
+#define AT91_MATRIX_MRCR (AT91_MATRIX + 0x100) /* Master Remap Control Register */
+#define AT91_MATRIX_RCB0 (1 << 0) /* Remap Command for AHB Master 0 (ARM926EJ-S Instruction Master) */
+#define AT91_MATRIX_RCB1 (1 << 1) /* Remap Command for AHB Master 1 (ARM926EJ-S Data Master) */
+#define AT91_MATRIX_RCB2 (1 << 2)
+#define AT91_MATRIX_RCB3 (1 << 3)
+#define AT91_MATRIX_RCB4 (1 << 4)
+#define AT91_MATRIX_RCB5 (1 << 5)
+#define AT91_MATRIX_RCB6 (1 << 6)
+#define AT91_MATRIX_RCB7 (1 << 7)
+#define AT91_MATRIX_RCB8 (1 << 8)
+#define AT91_MATRIX_RCB9 (1 << 9)
+#define AT91_MATRIX_RCB10 (1 << 10)
+#define AT91_MATRIX_RCB11 (1 << 11)
+
+#define AT91_MATRIX_TCMR (AT91_MATRIX + 0x110) /* TCM Configuration Register */
+#define AT91_MATRIX_ITCM_SIZE (0xf << 0) /* Size of ITCM enabled memory block */
+#define AT91_MATRIX_ITCM_0 (0 << 0)
+#define AT91_MATRIX_ITCM_32 (6 << 0)
+#define AT91_MATRIX_DTCM_SIZE (0xf << 4) /* Size of DTCM enabled memory block */
+#define AT91_MATRIX_DTCM_0 (0 << 4)
+#define AT91_MATRIX_DTCM_32 (6 << 4)
+#define AT91_MATRIX_DTCM_64 (7 << 4)
+#define AT91_MATRIX_TCM_NWS (0x1 << 11) /* Wait state TCM register */
+#define AT91_MATRIX_TCM_NO_WS (0x0 << 11)
+#define AT91_MATRIX_TCM_ONE_WS (0x1 << 11)
+
+#define AT91_MATRIX_VIDEO (AT91_MATRIX + 0x118) /* Video Mode Configuration Register */
+#define AT91C_VDEC_SEL (0x1 << 0) /* Video Mode Selection */
+#define AT91C_VDEC_SEL_OFF (0 << 0)
+#define AT91C_VDEC_SEL_ON (1 << 0)
+
+#define AT91_MATRIX_EBICSA (AT91_MATRIX + 0x128) /* EBI Chip Select Assignment Register */
+#define AT91_MATRIX_EBI_CS1A (1 << 1) /* Chip Select 1 Assignment */
+#define AT91_MATRIX_EBI_CS1A_SMC (0 << 1)
+#define AT91_MATRIX_EBI_CS1A_SDRAMC (1 << 1)
+#define AT91_MATRIX_EBI_CS3A (1 << 3) /* Chip Select 3 Assignment */
+#define AT91_MATRIX_EBI_CS3A_SMC (0 << 3)
+#define AT91_MATRIX_EBI_CS3A_SMC_SMARTMEDIA (1 << 3)
+#define AT91_MATRIX_EBI_CS4A (1 << 4) /* Chip Select 4 Assignment */
+#define AT91_MATRIX_EBI_CS4A_SMC (0 << 4)
+#define AT91_MATRIX_EBI_CS4A_SMC_CF0 (1 << 4)
+#define AT91_MATRIX_EBI_CS5A (1 << 5) /* Chip Select 5 Assignment */
+#define AT91_MATRIX_EBI_CS5A_SMC (0 << 5)
+#define AT91_MATRIX_EBI_CS5A_SMC_CF1 (1 << 5)
+#define AT91_MATRIX_EBI_DBPUC (1 << 8) /* Data Bus Pull-up Configuration */
+#define AT91_MATRIX_EBI_DBPU_ON (0 << 8)
+#define AT91_MATRIX_EBI_DBPU_OFF (1 << 8)
+#define AT91_MATRIX_EBI_VDDIOMSEL (1 << 16) /* Memory voltage selection */
+#define AT91_MATRIX_EBI_VDDIOMSEL_1_8V (0 << 16)
+#define AT91_MATRIX_EBI_VDDIOMSEL_3_3V (1 << 16)
+#define AT91_MATRIX_EBI_EBI_IOSR (1 << 17) /* EBI I/O slew rate selection */
+#define AT91_MATRIX_EBI_EBI_IOSR_REDUCED (0 << 17)
+#define AT91_MATRIX_EBI_EBI_IOSR_NORMAL (1 << 17)
+#define AT91_MATRIX_EBI_DDR_IOSR (1 << 18) /* DDR2 dedicated port I/O slew rate selection */
+#define AT91_MATRIX_EBI_DDR_IOSR_REDUCED (0 << 18)
+#define AT91_MATRIX_EBI_DDR_IOSR_NORMAL (1 << 18)
+
+#define AT91_MATRIX_WPMR (AT91_MATRIX + 0x1E4) /* Write Protect Mode Register */
+#define AT91_MATRIX_WPMR_WPEN (1 << 0) /* Write Protect ENable */
+#define AT91_MATRIX_WPMR_WP_WPDIS (0 << 0)
+#define AT91_MATRIX_WPMR_WP_WPEN (1 << 0)
+#define AT91_MATRIX_WPMR_WPKEY (0xFFFFFF << 8) /* Write Protect KEY */
+
+#define AT91_MATRIX_WPSR (AT91_MATRIX + 0x1E8) /* Write Protect Status Register */
+#define AT91_MATRIX_WPSR_WPVS (1 << 0) /* Write Protect Violation Status */
+#define AT91_MATRIX_WPSR_NO_WPV (0 << 0)
+#define AT91_MATRIX_WPSR_WPV (1 << 0)
+#define AT91_MATRIX_WPSR_WPVSRC (0xFFFF << 8) /* Write Protect Violation Source */
+
+#endif
diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index e6afff849b8..13f27a4b882 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -37,6 +37,7 @@
#include <linux/leds.h>
#include <linux/spi/spi.h>
#include <linux/usb/atmel_usba_udc.h>
+#include <sound/atmel-ac97c.h>
/* USB Device */
struct at91_udc_data {
@@ -80,7 +81,8 @@ struct at91_eth_data {
};
extern void __init at91_add_device_eth(struct at91_eth_data *data);
-#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9263) || defined(CONFIG_ARCH_AT91SAM9G20) || defined(CONFIG_ARCH_AT91CAP9)
+#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9263) || defined(CONFIG_ARCH_AT91SAM9G20) || defined(CONFIG_ARCH_AT91CAP9) \
+ || defined(CONFIG_ARCH_AT91SAM9G45)
#define eth_platform_data at91_eth_data
#endif
@@ -90,6 +92,7 @@ struct at91_usbh_data {
u8 vbus_pin[2]; /* port power-control pin */
};
extern void __init at91_add_device_usbh(struct at91_usbh_data *data);
+extern void __init at91_add_device_usbh_ohci(struct at91_usbh_data *data);
/* NAND / SmartMedia */
struct atmel_nand_data {
@@ -105,7 +108,11 @@ struct atmel_nand_data {
extern void __init at91_add_device_nand(struct atmel_nand_data *data);
/* I2C*/
+#if defined(CONFIG_ARCH_AT91SAM9G45)
+extern void __init at91_add_device_i2c(short i2c_id, struct i2c_board_info *devices, int nr_devices);
+#else
extern void __init at91_add_device_i2c(struct i2c_board_info *devices, int nr_devices);
+#endif
/* SPI */
extern void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices);
@@ -168,10 +175,7 @@ struct atmel_lcdfb_info;
extern void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data);
/* AC97 */
-struct atmel_ac97_data {
- u8 reset_pin; /* reset */
-};
-extern void __init at91_add_device_ac97(struct atmel_ac97_data *data);
+extern void __init at91_add_device_ac97(struct ac97c_platform_data *data);
/* ISI */
extern void __init at91_add_device_isi(void);
diff --git a/arch/arm/mach-at91/include/mach/cpu.h b/arch/arm/mach-at91/include/mach/cpu.h
index c554c3e4d55..34a9502c48b 100644
--- a/arch/arm/mach-at91/include/mach/cpu.h
+++ b/arch/arm/mach-at91/include/mach/cpu.h
@@ -21,8 +21,10 @@
#define ARCH_ID_AT91SAM9260 0x019803a0
#define ARCH_ID_AT91SAM9261 0x019703a0
#define ARCH_ID_AT91SAM9263 0x019607a0
+#define ARCH_ID_AT91SAM9G10 0x819903a0
#define ARCH_ID_AT91SAM9G20 0x019905a0
#define ARCH_ID_AT91SAM9RL64 0x019b03a0
+#define ARCH_ID_AT91SAM9G45 0x819b05a0
#define ARCH_ID_AT91CAP9 0x039A03A0
#define ARCH_ID_AT91SAM9XE128 0x329973a0
@@ -39,6 +41,15 @@ static inline unsigned long at91_cpu_identify(void)
return (at91_sys_read(AT91_DBGU_CIDR) & ~AT91_CIDR_VERSION);
}
+#define ARCH_EXID_AT91SAM9M11 0x00000001
+#define ARCH_EXID_AT91SAM9M10 0x00000002
+#define ARCH_EXID_AT91SAM9G45 0x00000004
+
+static inline unsigned long at91_exid_identify(void)
+{
+ return at91_sys_read(AT91_DBGU_EXID);
+}
+
#define ARCH_FAMILY_AT91X92 0x09200000
#define ARCH_FAMILY_AT91SAM9 0x01900000
@@ -87,6 +98,12 @@ static inline unsigned long at91cap9_rev_identify(void)
#define cpu_is_at91sam9261() (0)
#endif
+#ifdef CONFIG_ARCH_AT91SAM9G10
+#define cpu_is_at91sam9g10() (at91_cpu_identify() == ARCH_ID_AT91SAM9G10)
+#else
+#define cpu_is_at91sam9g10() (0)
+#endif
+
#ifdef CONFIG_ARCH_AT91SAM9263
#define cpu_is_at91sam9263() (at91_cpu_identify() == ARCH_ID_AT91SAM9263)
#else
@@ -99,6 +116,12 @@ static inline unsigned long at91cap9_rev_identify(void)
#define cpu_is_at91sam9rl() (0)
#endif
+#ifdef CONFIG_ARCH_AT91SAM9G45
+#define cpu_is_at91sam9g45() (at91_cpu_identify() == ARCH_ID_AT91SAM9G45)
+#else
+#define cpu_is_at91sam9g45() (0)
+#endif
+
#ifdef CONFIG_ARCH_AT91CAP9
#define cpu_is_at91cap9() (at91_cpu_identify() == ARCH_ID_AT91CAP9)
#define cpu_is_at91cap9_revB() (at91cap9_rev_identify() == ARCH_REVISION_CAP9_B)
diff --git a/arch/arm/mach-at91/include/mach/hardware.h b/arch/arm/mach-at91/include/mach/hardware.h
index da0b681c652..a0df8b022df 100644
--- a/arch/arm/mach-at91/include/mach/hardware.h
+++ b/arch/arm/mach-at91/include/mach/hardware.h
@@ -20,12 +20,14 @@
#include <mach/at91rm9200.h>
#elif defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
#include <mach/at91sam9260.h>
-#elif defined(CONFIG_ARCH_AT91SAM9261)
+#elif defined(CONFIG_ARCH_AT91SAM9261) || defined(CONFIG_ARCH_AT91SAM9G10)
#include <mach/at91sam9261.h>
#elif defined(CONFIG_ARCH_AT91SAM9263)
#include <mach/at91sam9263.h>
#elif defined(CONFIG_ARCH_AT91SAM9RL)
#include <mach/at91sam9rl.h>
+#elif defined(CONFIG_ARCH_AT91SAM9G45)
+#include <mach/at91sam9g45.h>
#elif defined(CONFIG_ARCH_AT91CAP9)
#include <mach/at91cap9.h>
#elif defined(CONFIG_ARCH_AT91X40)
diff --git a/arch/arm/mach-at91/include/mach/timex.h b/arch/arm/mach-at91/include/mach/timex.h
index d84c9948bec..31ac2d97f14 100644
--- a/arch/arm/mach-at91/include/mach/timex.h
+++ b/arch/arm/mach-at91/include/mach/timex.h
@@ -42,6 +42,11 @@
#define AT91SAM9_MASTER_CLOCK 99300000
#define CLOCK_TICK_RATE (AT91SAM9_MASTER_CLOCK/16)
+#elif defined(CONFIG_ARCH_AT91SAM9G10)
+
+#define AT91SAM9_MASTER_CLOCK 133000000
+#define CLOCK_TICK_RATE (AT91SAM9_MASTER_CLOCK/16)
+
#elif defined(CONFIG_ARCH_AT91SAM9263)
#if defined(CONFIG_MACH_USB_A9263)
@@ -62,6 +67,11 @@
#define AT91SAM9_MASTER_CLOCK 132096000
#define CLOCK_TICK_RATE (AT91SAM9_MASTER_CLOCK/16)
+#elif defined(CONFIG_ARCH_AT91SAM9G45)
+
+#define AT91SAM9_MASTER_CLOCK 133333333
+#define CLOCK_TICK_RATE (AT91SAM9_MASTER_CLOCK/16)
+
#elif defined(CONFIG_ARCH_AT91CAP9)
#define AT91CAP9_MASTER_CLOCK 100000000
diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
index e26c4fe61fa..4028724d490 100644
--- a/arch/arm/mach-at91/pm.c
+++ b/arch/arm/mach-at91/pm.c
@@ -201,7 +201,8 @@ static int at91_pm_verify_clocks(void)
pr_err("AT91: PM - Suspend-to-RAM with USB still active\n");
return 0;
}
- } else if (cpu_is_at91sam9260() || cpu_is_at91sam9261() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) {
+ } else if (cpu_is_at91sam9260() || cpu_is_at91sam9261() || cpu_is_at91sam9263()
+ || cpu_is_at91sam9g20() || cpu_is_at91sam9g10()) {
if ((scsr & (AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP)) != 0) {
pr_err("AT91: PM - Suspend-to-RAM with USB still active\n");
return 0;
diff --git a/arch/arm/mach-bcmring/Kconfig b/arch/arm/mach-bcmring/Kconfig
new file mode 100644
index 00000000000..457b4384913
--- /dev/null
+++ b/arch/arm/mach-bcmring/Kconfig
@@ -0,0 +1,21 @@
+choice
+ prompt "Processor selection in BCMRING family of devices"
+ depends on ARCH_BCMRING
+ default ARCH_BCM11107
+
+config ARCH_FPGA11107
+ bool "FPGA11107"
+
+config ARCH_BCM11107
+ bool "BCM11107"
+endchoice
+
+menu "BCMRING Options"
+ depends on ARCH_BCMRING
+
+config BCM_ZRELADDR
+ hex "Compressed ZREL ADDR"
+
+endmenu
+
+# source "drivers/char/bcmring/Kconfig"
diff --git a/arch/arm/mach-bcmring/Makefile b/arch/arm/mach-bcmring/Makefile
new file mode 100644
index 00000000000..f8d9fcedf91
--- /dev/null
+++ b/arch/arm/mach-bcmring/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+obj-y := arch.o mm.o irq.o clock.o core.o timer.o dma.o
+obj-y += csp/
diff --git a/arch/arm/mach-bcmring/Makefile.boot b/arch/arm/mach-bcmring/Makefile.boot
new file mode 100644
index 00000000000..fb53b283beb
--- /dev/null
+++ b/arch/arm/mach-bcmring/Makefile.boot
@@ -0,0 +1,6 @@
+# Address where decompressor will be written and eventually executed.
+#
+# default to SDRAM
+zreladdr-y := $(CONFIG_BCM_ZRELADDR)
+params_phys-y := 0x00000800
+
diff --git a/arch/arm/mach-bcmring/arch.c b/arch/arm/mach-bcmring/arch.c
new file mode 100644
index 00000000000..0da693b0f7e
--- /dev/null
+++ b/arch/arm/mach-bcmring/arch.c
@@ -0,0 +1,157 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+
+#include <linux/proc_fs.h>
+#include <linux/sysctl.h>
+
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+#include <asm/mach/time.h>
+
+#include <asm/mach/arch.h>
+#include <mach/dma.h>
+#include <mach/hardware.h>
+#include <mach/csp/mm_io.h>
+#include <mach/csp/chipcHw_def.h>
+#include <mach/csp/chipcHw_inline.h>
+
+#include <cfg_global.h>
+
+#include "core.h"
+
+HW_DECLARE_SPINLOCK(arch)
+HW_DECLARE_SPINLOCK(gpio)
+#if defined(CONFIG_DEBUG_SPINLOCK)
+ EXPORT_SYMBOL(bcmring_gpio_reg_lock);
+#endif
+
+/* FIXME: temporary solution */
+#define BCM_SYSCTL_REBOOT_WARM 1
+#define CTL_BCM_REBOOT 112
+
+/* sysctl */
+int bcmring_arch_warm_reboot; /* do a warm reboot on hard reset */
+
+static struct ctl_table_header *bcmring_sysctl_header;
+
+static struct ctl_table bcmring_sysctl_warm_reboot[] = {
+ {
+ .ctl_name = BCM_SYSCTL_REBOOT_WARM,
+ .procname = "warm",
+ .data = &bcmring_arch_warm_reboot,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec},
+ {}
+};
+
+static struct ctl_table bcmring_sysctl_reboot[] = {
+ {
+ .ctl_name = CTL_BCM_REBOOT,
+ .procname = "reboot",
+ .mode = 0555,
+ .child = bcmring_sysctl_warm_reboot},
+ {}
+};
+
+static struct platform_device nand_device = {
+ .name = "bcm-nand",
+ .id = -1,
+};
+
+static struct platform_device *devices[] __initdata = {
+ &nand_device,
+};
+
+/****************************************************************************
+*
+* Called from the customize_machine function in arch/arm/kernel/setup.c
+*
+* The customize_machine function is tagged as an arch_initcall
+* (see include/linux/init.h for the order that the various init sections
+* are called in.
+*
+*****************************************************************************/
+static void __init bcmring_init_machine(void)
+{
+
+ bcmring_sysctl_header = register_sysctl_table(bcmring_sysctl_reboot);
+
+ /* Enable spread spectrum */
+ chipcHw_enableSpreadSpectrum();
+
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+
+ bcmring_amba_init();
+
+ dma_init();
+}
+
+/****************************************************************************
+*
+* Called from setup_arch (in arch/arm/kernel/setup.c) to fixup any tags
+* passed in by the boot loader.
+*
+*****************************************************************************/
+
+static void __init bcmring_fixup(struct machine_desc *desc,
+ struct tag *t, char **cmdline, struct meminfo *mi) {
+#ifdef CONFIG_BLK_DEV_INITRD
+ printk(KERN_NOTICE "bcmring_fixup\n");
+ t->hdr.tag = ATAG_CORE;
+ t->hdr.size = tag_size(tag_core);
+ t->u.core.flags = 0;
+ t->u.core.pagesize = PAGE_SIZE;
+ t->u.core.rootdev = 31 << 8 | 0;
+ t = tag_next(t);
+
+ t->hdr.tag = ATAG_MEM;
+ t->hdr.size = tag_size(tag_mem32);
+ t->u.mem.start = CFG_GLOBAL_RAM_BASE;
+ t->u.mem.size = CFG_GLOBAL_RAM_SIZE;
+
+ t = tag_next(t);
+
+ t->hdr.tag = ATAG_NONE;
+ t->hdr.size = 0;
+#endif
+}
+
+/****************************************************************************
+*
+* Machine Description
+*
+*****************************************************************************/
+
+MACHINE_START(BCMRING, "BCMRING")
+ /* Maintainer: Broadcom Corporation */
+ .phys_io = MM_IO_START,
+ .io_pg_offst = (MM_IO_BASE >> 18) & 0xfffc,
+ .fixup = bcmring_fixup,
+ .map_io = bcmring_map_io,
+ .init_irq = bcmring_init_irq,
+ .timer = &bcmring_timer,
+ .init_machine = bcmring_init_machine
+MACHINE_END
diff --git a/arch/arm/mach-bcmring/clock.c b/arch/arm/mach-bcmring/clock.c
new file mode 100644
index 00000000000..14bafc38f2d
--- /dev/null
+++ b/arch/arm/mach-bcmring/clock.c
@@ -0,0 +1,224 @@
+/*****************************************************************************
+* Copyright 2001 - 2009 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+#include <mach/csp/hw_cfg.h>
+#include <mach/csp/chipcHw_def.h>
+#include <mach/csp/chipcHw_reg.h>
+#include <mach/csp/chipcHw_inline.h>
+
+#include <asm/clkdev.h>
+
+#include "clock.h"
+
+#define clk_is_primary(x) ((x)->type & CLK_TYPE_PRIMARY)
+#define clk_is_pll1(x) ((x)->type & CLK_TYPE_PLL1)
+#define clk_is_pll2(x) ((x)->type & CLK_TYPE_PLL2)
+#define clk_is_programmable(x) ((x)->type & CLK_TYPE_PROGRAMMABLE)
+#define clk_is_bypassable(x) ((x)->type & CLK_TYPE_BYPASSABLE)
+
+#define clk_is_using_xtal(x) ((x)->mode & CLK_MODE_XTAL)
+
+static DEFINE_SPINLOCK(clk_lock);
+
+static void __clk_enable(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ /* enable parent clock first */
+ if (clk->parent)
+ __clk_enable(clk->parent);
+
+ if (clk->use_cnt++ == 0) {
+ if (clk_is_pll1(clk)) { /* PLL1 */
+ chipcHw_pll1Enable(clk->rate_hz, 0);
+ } else if (clk_is_pll2(clk)) { /* PLL2 */
+ chipcHw_pll2Enable(clk->rate_hz);
+ } else if (clk_is_using_xtal(clk)) { /* source is crystal */
+ if (!clk_is_primary(clk))
+ chipcHw_bypassClockEnable(clk->csp_id);
+ } else { /* source is PLL */
+ chipcHw_setClockEnable(clk->csp_id);
+ }
+ }
+}
+
+int clk_enable(struct clk *clk)
+{
+ unsigned long flags;
+
+ if (!clk)
+ return -EINVAL;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ __clk_enable(clk);
+ spin_unlock_irqrestore(&clk_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+static void __clk_disable(struct clk *clk)
+{
+ if (!clk)
+ return;
+
+ BUG_ON(clk->use_cnt == 0);
+
+ if (--clk->use_cnt == 0) {
+ if (clk_is_pll1(clk)) { /* PLL1 */
+ chipcHw_pll1Disable();
+ } else if (clk_is_pll2(clk)) { /* PLL2 */
+ chipcHw_pll2Disable();
+ } else if (clk_is_using_xtal(clk)) { /* source is crystal */
+ if (!clk_is_primary(clk))
+ chipcHw_bypassClockDisable(clk->csp_id);
+ } else { /* source is PLL */
+ chipcHw_setClockDisable(clk->csp_id);
+ }
+ }
+
+ if (clk->parent)
+ __clk_disable(clk->parent);
+}
+
+void clk_disable(struct clk *clk)
+{
+ unsigned long flags;
+
+ if (!clk)
+ return;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ __clk_disable(clk);
+ spin_unlock_irqrestore(&clk_lock, flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (!clk)
+ return 0;
+
+ return clk->rate_hz;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long flags;
+ unsigned long actual;
+ unsigned long rate_hz;
+
+ if (!clk)
+ return -EINVAL;
+
+ if (!clk_is_programmable(clk))
+ return -EINVAL;
+
+ if (clk->use_cnt)
+ return -EBUSY;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ actual = clk->parent->rate_hz;
+ rate_hz = min(actual, rate);
+ spin_unlock_irqrestore(&clk_lock, flags);
+
+ return rate_hz;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long flags;
+ unsigned long actual;
+ unsigned long rate_hz;
+
+ if (!clk)
+ return -EINVAL;
+
+ if (!clk_is_programmable(clk))
+ return -EINVAL;
+
+ if (clk->use_cnt)
+ return -EBUSY;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ actual = clk->parent->rate_hz;
+ rate_hz = min(actual, rate);
+ rate_hz = chipcHw_setClockFrequency(clk->csp_id, rate_hz);
+ clk->rate_hz = rate_hz;
+ spin_unlock_irqrestore(&clk_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+ if (!clk)
+ return NULL;
+
+ return clk->parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ unsigned long flags;
+ struct clk *old_parent;
+
+ if (!clk || !parent)
+ return -EINVAL;
+
+ if (!clk_is_primary(parent) || !clk_is_bypassable(clk))
+ return -EINVAL;
+
+ /* if more than one user, parent is not allowed */
+ if (clk->use_cnt > 1)
+ return -EBUSY;
+
+ if (clk->parent == parent)
+ return 0;
+
+ spin_lock_irqsave(&clk_lock, flags);
+ old_parent = clk->parent;
+ clk->parent = parent;
+ if (clk_is_using_xtal(parent))
+ clk->mode |= CLK_MODE_XTAL;
+ else
+ clk->mode &= (~CLK_MODE_XTAL);
+
+ /* if clock is active */
+ if (clk->use_cnt != 0) {
+ clk->use_cnt--;
+ /* enable clock with the new parent */
+ __clk_enable(clk);
+ /* disable the old parent */
+ __clk_disable(old_parent);
+ }
+ spin_unlock_irqrestore(&clk_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(clk_set_parent);
diff --git a/arch/arm/mach-bcmring/clock.h b/arch/arm/mach-bcmring/clock.h
new file mode 100644
index 00000000000..5e0b9813897
--- /dev/null
+++ b/arch/arm/mach-bcmring/clock.h
@@ -0,0 +1,33 @@
+/*****************************************************************************
+* Copyright 2001 - 2009 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+#include <mach/csp/chipcHw_def.h>
+
+#define CLK_TYPE_PRIMARY 1 /* primary clock must NOT have a parent */
+#define CLK_TYPE_PLL1 2 /* PPL1 */
+#define CLK_TYPE_PLL2 4 /* PPL2 */
+#define CLK_TYPE_PROGRAMMABLE 8 /* programmable clock rate */
+#define CLK_TYPE_BYPASSABLE 16 /* parent can be changed */
+
+#define CLK_MODE_XTAL 1 /* clock source is from crystal */
+
+struct clk {
+ const char *name; /* clock name */
+ unsigned int type; /* clock type */
+ unsigned int mode; /* current mode */
+ volatile int use_bypass; /* indicate if it's in bypass mode */
+ chipcHw_CLOCK_e csp_id; /* clock ID for CSP CHIPC */
+ unsigned long rate_hz; /* clock rate in Hz */
+ unsigned int use_cnt; /* usage count */
+ struct clk *parent; /* parent clock */
+};
diff --git a/arch/arm/mach-bcmring/core.c b/arch/arm/mach-bcmring/core.c
new file mode 100644
index 00000000000..492c649f451
--- /dev/null
+++ b/arch/arm/mach-bcmring/core.c
@@ -0,0 +1,367 @@
+/*
+ * derived from linux/arch/arm/mach-versatile/core.c
+ * linux/arch/arm/mach-bcmring/core.c
+ *
+ * Copyright (C) 1999 - 2003 ARM Limited
+ * Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* Portions copyright Broadcom 2008 */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/sysdev.h>
+#include <linux/interrupt.h>
+#include <linux/amba/bus.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+
+#include <linux/amba/bus.h>
+#include <mach/csp/mm_addr.h>
+#include <mach/hardware.h>
+#include <asm/clkdev.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <asm/hardware/arm_timer.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
+#include <asm/mach/irq.h>
+#include <asm/mach/time.h>
+#include <asm/mach/map.h>
+#include <asm/mach/mmc.h>
+
+#include <cfg_global.h>
+
+#include "clock.h"
+
+#include <csp/secHw.h>
+#include <mach/csp/secHw_def.h>
+#include <mach/csp/chipcHw_inline.h>
+#include <mach/csp/tmrHw_reg.h>
+
+#define AMBA_DEVICE(name, initname, base, plat, size) \
+static struct amba_device name##_device = { \
+ .dev = { \
+ .coherent_dma_mask = ~0, \
+ .init_name = initname, \
+ .platform_data = plat \
+ }, \
+ .res = { \
+ .start = MM_ADDR_IO_##base, \
+ .end = MM_ADDR_IO_##base + (size) - 1, \
+ .flags = IORESOURCE_MEM \
+ }, \
+ .dma_mask = ~0, \
+ .irq = { \
+ IRQ_##base \
+ } \
+}
+
+
+AMBA_DEVICE(uartA, "uarta", UARTA, NULL, SZ_4K);
+AMBA_DEVICE(uartB, "uartb", UARTB, NULL, SZ_4K);
+
+static struct clk pll1_clk = {
+ .name = "PLL1",
+ .type = CLK_TYPE_PRIMARY | CLK_TYPE_PLL1,
+ .rate_hz = 2000000000,
+ .use_cnt = 7,
+};
+
+static struct clk uart_clk = {
+ .name = "UART",
+ .type = CLK_TYPE_PROGRAMMABLE,
+ .csp_id = chipcHw_CLOCK_UART,
+ .rate_hz = HW_CFG_UART_CLK_HZ,
+ .parent = &pll1_clk,
+};
+
+static struct clk_lookup lookups[] = {
+ { /* UART0 */
+ .dev_id = "uarta",
+ .clk = &uart_clk,
+ }, { /* UART1 */
+ .dev_id = "uartb",
+ .clk = &uart_clk,
+ }
+};
+
+static struct amba_device *amba_devs[] __initdata = {
+ &uartA_device,
+ &uartB_device,
+};
+
+void __init bcmring_amba_init(void)
+{
+ int i;
+ u32 bus_clock;
+
+/* Linux is run initially in non-secure mode. Secure peripherals */
+/* generate FIQ, and must be handled in secure mode. Until we have */
+/* a linux security monitor implementation, keep everything in */
+/* non-secure mode. */
+ chipcHw_busInterfaceClockEnable(chipcHw_REG_BUS_CLOCK_SPU);
+ secHw_setUnsecure(secHw_BLK_MASK_CHIP_CONTROL |
+ secHw_BLK_MASK_KEY_SCAN |
+ secHw_BLK_MASK_TOUCH_SCREEN |
+ secHw_BLK_MASK_UART0 |
+ secHw_BLK_MASK_UART1 |
+ secHw_BLK_MASK_WATCHDOG |
+ secHw_BLK_MASK_SPUM |
+ secHw_BLK_MASK_DDR2 |
+ secHw_BLK_MASK_SPU |
+ secHw_BLK_MASK_PKA |
+ secHw_BLK_MASK_RNG |
+ secHw_BLK_MASK_RTC |
+ secHw_BLK_MASK_OTP |
+ secHw_BLK_MASK_BOOT |
+ secHw_BLK_MASK_MPU |
+ secHw_BLK_MASK_TZCTRL | secHw_BLK_MASK_INTR);
+
+ /* Only the devices attached to the AMBA bus are enabled just before the bus is */
+ /* scanned and the drivers are loaded. The clocks need to be on for the AMBA bus */
+ /* driver to access these blocks. The bus is probed, and the drivers are loaded. */
+ /* FIXME Need to remove enable of PIF once CLCD clock enable used properly in FPGA. */
+ bus_clock = chipcHw_REG_BUS_CLOCK_GE
+ | chipcHw_REG_BUS_CLOCK_SDIO0 | chipcHw_REG_BUS_CLOCK_SDIO1;
+
+ chipcHw_busInterfaceClockEnable(bus_clock);
+
+ for (i = 0; i < ARRAY_SIZE(lookups); i++)
+ clkdev_add(&lookups[i]);
+
+ for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
+ struct amba_device *d = amba_devs[i];
+ amba_device_register(d, &iomem_resource);
+ }
+}
+
+/*
+ * Where is the timer (VA)?
+ */
+#define TIMER0_VA_BASE MM_IO_BASE_TMR
+#define TIMER1_VA_BASE (MM_IO_BASE_TMR + 0x20)
+#define TIMER2_VA_BASE (MM_IO_BASE_TMR + 0x40)
+#define TIMER3_VA_BASE (MM_IO_BASE_TMR + 0x60)
+
+/* Timer 0 - 25 MHz, Timer3 at bus clock rate, typically 150-166 MHz */
+#if defined(CONFIG_ARCH_FPGA11107)
+/* fpga cpu/bus are currently 30 times slower so scale frequency as well to */
+/* slow down Linux's sense of time */
+#define TIMER0_FREQUENCY_MHZ (tmrHw_LOW_FREQUENCY_MHZ * 30)
+#define TIMER1_FREQUENCY_MHZ (tmrHw_LOW_FREQUENCY_MHZ * 30)
+#define TIMER3_FREQUENCY_MHZ (tmrHw_HIGH_FREQUENCY_MHZ * 30)
+#define TIMER3_FREQUENCY_KHZ (tmrHw_HIGH_FREQUENCY_HZ / 1000 * 30)
+#else
+#define TIMER0_FREQUENCY_MHZ tmrHw_LOW_FREQUENCY_MHZ
+#define TIMER1_FREQUENCY_MHZ tmrHw_LOW_FREQUENCY_MHZ
+#define TIMER3_FREQUENCY_MHZ tmrHw_HIGH_FREQUENCY_MHZ
+#define TIMER3_FREQUENCY_KHZ (tmrHw_HIGH_FREQUENCY_HZ / 1000)
+#endif
+
+#define TICKS_PER_uSEC TIMER0_FREQUENCY_MHZ
+
+/*
+ * These are useconds NOT ticks.
+ *
+ */
+#define mSEC_1 1000
+#define mSEC_5 (mSEC_1 * 5)
+#define mSEC_10 (mSEC_1 * 10)
+#define mSEC_25 (mSEC_1 * 25)
+#define SEC_1 (mSEC_1 * 1000)
+
+/*
+ * How long is the timer interval?
+ */
+#define TIMER_INTERVAL (TICKS_PER_uSEC * mSEC_10)
+#if TIMER_INTERVAL >= 0x100000
+#define TIMER_RELOAD (TIMER_INTERVAL >> 8)
+#define TIMER_DIVISOR (TIMER_CTRL_DIV256)
+#define TICKS2USECS(x) (256 * (x) / TICKS_PER_uSEC)
+#elif TIMER_INTERVAL >= 0x10000
+#define TIMER_RELOAD (TIMER_INTERVAL >> 4) /* Divide by 16 */
+#define TIMER_DIVISOR (TIMER_CTRL_DIV16)
+#define TICKS2USECS(x) (16 * (x) / TICKS_PER_uSEC)
+#else
+#define TIMER_RELOAD (TIMER_INTERVAL)
+#define TIMER_DIVISOR (TIMER_CTRL_DIV1)
+#define TICKS2USECS(x) ((x) / TICKS_PER_uSEC)
+#endif
+
+static void timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *clk)
+{
+ unsigned long ctrl;
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ writel(TIMER_RELOAD, TIMER0_VA_BASE + TIMER_LOAD);
+
+ ctrl = TIMER_CTRL_PERIODIC;
+ ctrl |=
+ TIMER_DIVISOR | TIMER_CTRL_32BIT | TIMER_CTRL_IE |
+ TIMER_CTRL_ENABLE;
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ /* period set, and timer enabled in 'next_event' hook */
+ ctrl = TIMER_CTRL_ONESHOT;
+ ctrl |= TIMER_DIVISOR | TIMER_CTRL_32BIT | TIMER_CTRL_IE;
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ default:
+ ctrl = 0;
+ }
+
+ writel(ctrl, TIMER0_VA_BASE + TIMER_CTRL);
+}
+
+static int timer_set_next_event(unsigned long evt,
+ struct clock_event_device *unused)
+{
+ unsigned long ctrl = readl(TIMER0_VA_BASE + TIMER_CTRL);
+
+ writel(evt, TIMER0_VA_BASE + TIMER_LOAD);
+ writel(ctrl | TIMER_CTRL_ENABLE, TIMER0_VA_BASE + TIMER_CTRL);
+
+ return 0;
+}
+
+static struct clock_event_device timer0_clockevent = {
+ .name = "timer0",
+ .shift = 32,
+ .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_mode = timer_set_mode,
+ .set_next_event = timer_set_next_event,
+};
+
+/*
+ * IRQ handler for the timer
+ */
+static irqreturn_t bcmring_timer_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = &timer0_clockevent;
+
+ writel(1, TIMER0_VA_BASE + TIMER_INTCLR);
+
+ evt->event_handler(evt);
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction bcmring_timer_irq = {
+ .name = "bcmring Timer Tick",
+ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
+ .handler = bcmring_timer_interrupt,
+};
+
+static cycle_t bcmring_get_cycles_timer1(void)
+{
+ return ~readl(TIMER1_VA_BASE + TIMER_VALUE);
+}
+
+static cycle_t bcmring_get_cycles_timer3(void)
+{
+ return ~readl(TIMER3_VA_BASE + TIMER_VALUE);
+}
+
+static struct clocksource clocksource_bcmring_timer1 = {
+ .name = "timer1",
+ .rating = 200,
+ .read = bcmring_get_cycles_timer1,
+ .mask = CLOCKSOURCE_MASK(32),
+ .shift = 20,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static struct clocksource clocksource_bcmring_timer3 = {
+ .name = "timer3",
+ .rating = 100,
+ .read = bcmring_get_cycles_timer3,
+ .mask = CLOCKSOURCE_MASK(32),
+ .shift = 20,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static int __init bcmring_clocksource_init(void)
+{
+ /* setup timer1 as free-running clocksource */
+ writel(0, TIMER1_VA_BASE + TIMER_CTRL);
+ writel(0xffffffff, TIMER1_VA_BASE + TIMER_LOAD);
+ writel(0xffffffff, TIMER1_VA_BASE + TIMER_VALUE);
+ writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC,
+ TIMER1_VA_BASE + TIMER_CTRL);
+
+ clocksource_bcmring_timer1.mult =
+ clocksource_khz2mult(TIMER1_FREQUENCY_MHZ * 1000,
+ clocksource_bcmring_timer1.shift);
+ clocksource_register(&clocksource_bcmring_timer1);
+
+ /* setup timer3 as free-running clocksource */
+ writel(0, TIMER3_VA_BASE + TIMER_CTRL);
+ writel(0xffffffff, TIMER3_VA_BASE + TIMER_LOAD);
+ writel(0xffffffff, TIMER3_VA_BASE + TIMER_VALUE);
+ writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC,
+ TIMER3_VA_BASE + TIMER_CTRL);
+
+ clocksource_bcmring_timer3.mult =
+ clocksource_khz2mult(TIMER3_FREQUENCY_KHZ,
+ clocksource_bcmring_timer3.shift);
+ clocksource_register(&clocksource_bcmring_timer3);
+
+ return 0;
+}
+
+/*
+ * Set up timer interrupt, and return the current time in seconds.
+ */
+void __init bcmring_init_timer(void)
+{
+ printk(KERN_INFO "bcmring_init_timer\n");
+ /*
+ * Initialise to a known state (all timers off)
+ */
+ writel(0, TIMER0_VA_BASE + TIMER_CTRL);
+ writel(0, TIMER1_VA_BASE + TIMER_CTRL);
+ writel(0, TIMER2_VA_BASE + TIMER_CTRL);
+ writel(0, TIMER3_VA_BASE + TIMER_CTRL);
+
+ /*
+ * Make irqs happen for the system timer
+ */
+ setup_irq(IRQ_TIMER0, &bcmring_timer_irq);
+
+ bcmring_clocksource_init();
+
+ timer0_clockevent.mult =
+ div_sc(1000000, NSEC_PER_SEC, timer0_clockevent.shift);
+ timer0_clockevent.max_delta_ns =
+ clockevent_delta2ns(0xffffffff, &timer0_clockevent);
+ timer0_clockevent.min_delta_ns =
+ clockevent_delta2ns(0xf, &timer0_clockevent);
+
+ timer0_clockevent.cpumask = cpumask_of(0);
+ clockevents_register_device(&timer0_clockevent);
+}
+
+struct sys_timer bcmring_timer = {
+ .init = bcmring_init_timer,
+};
diff --git a/arch/arm/mach-bcmring/core.h b/arch/arm/mach-bcmring/core.h
new file mode 100644
index 00000000000..b197ba48e36
--- /dev/null
+++ b/arch/arm/mach-bcmring/core.h
@@ -0,0 +1,30 @@
+/*
+ * linux/arch/arm/mach-versatile/core.h
+ *
+ * Copyright (C) 2004 ARM Limited
+ * Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* Portions copyright Broadcom 2008 */
+#ifndef __ASM_ARCH_BCMRING_H
+#define __ASM_ARCH_BCMRING_H
+
+void __init bcmring_amba_init(void);
+void __init bcmring_map_io(void);
+void __init bcmring_init_irq(void);
+
+extern struct sys_timer bcmring_timer;
+#endif
diff --git a/arch/arm/mach-bcmring/csp/Makefile b/arch/arm/mach-bcmring/csp/Makefile
new file mode 100644
index 00000000000..648c0377530
--- /dev/null
+++ b/arch/arm/mach-bcmring/csp/Makefile
@@ -0,0 +1,3 @@
+obj-y += dmac/
+obj-y += tmr/
+obj-y += chipc/
diff --git a/arch/arm/mach-bcmring/csp/chipc/Makefile b/arch/arm/mach-bcmring/csp/chipc/Makefile
new file mode 100644
index 00000000000..673952768ee
--- /dev/null
+++ b/arch/arm/mach-bcmring/csp/chipc/Makefile
@@ -0,0 +1 @@
+obj-y += chipcHw.o chipcHw_str.o chipcHw_reset.o chipcHw_init.o
diff --git a/arch/arm/mach-bcmring/csp/chipc/chipcHw.c b/arch/arm/mach-bcmring/csp/chipc/chipcHw.c
new file mode 100644
index 00000000000..b3a61d860c6
--- /dev/null
+++ b/arch/arm/mach-bcmring/csp/chipc/chipcHw.c
@@ -0,0 +1,776 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file chipcHw.c
+*
+* @brief Low level Various CHIP clock controlling routines
+*
+* @note
+*
+* These routines provide basic clock controlling functionality only.
+*/
+/****************************************************************************/
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <csp/errno.h>
+#include <csp/stdint.h>
+#include <csp/module.h>
+
+#include <mach/csp/chipcHw_def.h>
+#include <mach/csp/chipcHw_inline.h>
+
+#include <csp/reg.h>
+#include <csp/delay.h>
+
+/* ---- Private Constants and Types --------------------------------------- */
+
+/* VPM alignment algorithm uses this */
+#define MAX_PHASE_ADJUST_COUNT 0xFFFF /* Max number of times allowed to adjust the phase */
+#define MAX_PHASE_ALIGN_ATTEMPTS 10 /* Max number of attempt to align the phase */
+
+/* Local definition of clock type */
+#define PLL_CLOCK 1 /* PLL Clock */
+#define NON_PLL_CLOCK 2 /* Divider clock */
+
+static int chipcHw_divide(int num, int denom)
+ __attribute__ ((section(".aramtext")));
+
+/****************************************************************************/
+/**
+* @brief Set clock fequency for miscellaneous configurable clocks
+*
+* This function sets clock frequency
+*
+* @return Configured clock frequency in hertz
+*
+*/
+/****************************************************************************/
+chipcHw_freq chipcHw_getClockFrequency(chipcHw_CLOCK_e clock /* [ IN ] Configurable clock */
+ ) {
+ volatile uint32_t *pPLLReg = (uint32_t *) 0x0;
+ volatile uint32_t *pClockCtrl = (uint32_t *) 0x0;
+ volatile uint32_t *pDependentClock = (uint32_t *) 0x0;
+ uint32_t vcoFreqPll1Hz = 0; /* Effective VCO frequency for PLL1 in Hz */
+ uint32_t vcoFreqPll2Hz = 0; /* Effective VCO frequency for PLL2 in Hz */
+ uint32_t dependentClockType = 0;
+ uint32_t vcoHz = 0;
+
+ /* Get VCO frequencies */
+ if ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MASK) != chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_INTEGER) {
+ uint64_t adjustFreq = 0;
+
+ vcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
+ chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
+ ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
+
+ /* Adjusted frequency due to chipcHw_REG_PLL_DIVIDER_NDIV_f_SS */
+ adjustFreq = (uint64_t) chipcHw_XTAL_FREQ_Hz *
+ (uint64_t) chipcHw_REG_PLL_DIVIDER_NDIV_f_SS *
+ chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, (chipcHw_REG_PLL_PREDIVIDER_P2 * (uint64_t) chipcHw_REG_PLL_DIVIDER_FRAC));
+ vcoFreqPll1Hz += (uint32_t) adjustFreq;
+ } else {
+ vcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
+ chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
+ ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
+ }
+ vcoFreqPll2Hz =
+ chipcHw_XTAL_FREQ_Hz *
+ chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
+ ((pChipcHw->PLLPreDivider2 & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
+
+ switch (clock) {
+ case chipcHw_CLOCK_DDR:
+ pPLLReg = &pChipcHw->DDRClock;
+ vcoHz = vcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_ARM:
+ pPLLReg = &pChipcHw->ARMClock;
+ vcoHz = vcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_ESW:
+ pPLLReg = &pChipcHw->ESWClock;
+ vcoHz = vcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_VPM:
+ pPLLReg = &pChipcHw->VPMClock;
+ vcoHz = vcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_ESW125:
+ pPLLReg = &pChipcHw->ESW125Clock;
+ vcoHz = vcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_UART:
+ pPLLReg = &pChipcHw->UARTClock;
+ vcoHz = vcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_SDIO0:
+ pPLLReg = &pChipcHw->SDIO0Clock;
+ vcoHz = vcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_SDIO1:
+ pPLLReg = &pChipcHw->SDIO1Clock;
+ vcoHz = vcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_SPI:
+ pPLLReg = &pChipcHw->SPIClock;
+ vcoHz = vcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_ETM:
+ pPLLReg = &pChipcHw->ETMClock;
+ vcoHz = vcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_USB:
+ pPLLReg = &pChipcHw->USBClock;
+ vcoHz = vcoFreqPll2Hz;
+ break;
+ case chipcHw_CLOCK_LCD:
+ pPLLReg = &pChipcHw->LCDClock;
+ vcoHz = vcoFreqPll2Hz;
+ break;
+ case chipcHw_CLOCK_APM:
+ pPLLReg = &pChipcHw->APMClock;
+ vcoHz = vcoFreqPll2Hz;
+ break;
+ case chipcHw_CLOCK_BUS:
+ pClockCtrl = &pChipcHw->ACLKClock;
+ pDependentClock = &pChipcHw->ARMClock;
+ vcoHz = vcoFreqPll1Hz;
+ dependentClockType = PLL_CLOCK;
+ break;
+ case chipcHw_CLOCK_OTP:
+ pClockCtrl = &pChipcHw->OTPClock;
+ break;
+ case chipcHw_CLOCK_I2C:
+ pClockCtrl = &pChipcHw->I2CClock;
+ break;
+ case chipcHw_CLOCK_I2S0:
+ pClockCtrl = &pChipcHw->I2S0Clock;
+ break;
+ case chipcHw_CLOCK_RTBUS:
+ pClockCtrl = &pChipcHw->RTBUSClock;
+ pDependentClock = &pChipcHw->ACLKClock;
+ dependentClockType = NON_PLL_CLOCK;
+ break;
+ case chipcHw_CLOCK_APM100:
+ pClockCtrl = &pChipcHw->APM100Clock;
+ pDependentClock = &pChipcHw->APMClock;
+ vcoHz = vcoFreqPll2Hz;
+ dependentClockType = PLL_CLOCK;
+ break;
+ case chipcHw_CLOCK_TSC:
+ pClockCtrl = &pChipcHw->TSCClock;
+ break;
+ case chipcHw_CLOCK_LED:
+ pClockCtrl = &pChipcHw->LEDClock;
+ break;
+ case chipcHw_CLOCK_I2S1:
+ pClockCtrl = &pChipcHw->I2S1Clock;
+ break;
+ }
+
+ if (pPLLReg) {
+ /* Obtain PLL clock frequency */
+ if (*pPLLReg & chipcHw_REG_PLL_CLOCK_BYPASS_SELECT) {
+ /* Return crystal clock frequency when bypassed */
+ return chipcHw_XTAL_FREQ_Hz;
+ } else if (clock == chipcHw_CLOCK_DDR) {
+ /* DDR frequency is configured in PLLDivider register */
+ return chipcHw_divide (vcoHz, (((pChipcHw->PLLDivider & 0xFF000000) >> 24) ? ((pChipcHw->PLLDivider & 0xFF000000) >> 24) : 256));
+ } else {
+ /* From chip revision number B0, LCD clock is internally divided by 2 */
+ if ((pPLLReg == &pChipcHw->LCDClock) && (chipcHw_getChipRevisionNumber() != chipcHw_REV_NUMBER_A0)) {
+ vcoHz >>= 1;
+ }
+ /* Obtain PLL clock frequency using VCO dividers */
+ return chipcHw_divide(vcoHz, ((*pPLLReg & chipcHw_REG_PLL_CLOCK_MDIV_MASK) ? (*pPLLReg & chipcHw_REG_PLL_CLOCK_MDIV_MASK) : 256));
+ }
+ } else if (pClockCtrl) {
+ /* Obtain divider clock frequency */
+ uint32_t div;
+ uint32_t freq = 0;
+
+ if (*pClockCtrl & chipcHw_REG_DIV_CLOCK_BYPASS_SELECT) {
+ /* Return crystal clock frequency when bypassed */
+ return chipcHw_XTAL_FREQ_Hz;
+ } else if (pDependentClock) {
+ /* Identify the dependent clock frequency */
+ switch (dependentClockType) {
+ case PLL_CLOCK:
+ if (*pDependentClock & chipcHw_REG_PLL_CLOCK_BYPASS_SELECT) {
+ /* Use crystal clock frequency when dependent PLL clock is bypassed */
+ freq = chipcHw_XTAL_FREQ_Hz;
+ } else {
+ /* Obtain PLL clock frequency using VCO dividers */
+ div = *pDependentClock & chipcHw_REG_PLL_CLOCK_MDIV_MASK;
+ freq = div ? chipcHw_divide(vcoHz, div) : 0;
+ }
+ break;
+ case NON_PLL_CLOCK:
+ if (pDependentClock == (uint32_t *) &pChipcHw->ACLKClock) {
+ freq = chipcHw_getClockFrequency (chipcHw_CLOCK_BUS);
+ } else {
+ if (*pDependentClock & chipcHw_REG_DIV_CLOCK_BYPASS_SELECT) {
+ /* Use crystal clock frequency when dependent divider clock is bypassed */
+ freq = chipcHw_XTAL_FREQ_Hz;
+ } else {
+ /* Obtain divider clock frequency using XTAL dividers */
+ div = *pDependentClock & chipcHw_REG_DIV_CLOCK_DIV_MASK;
+ freq = chipcHw_divide (chipcHw_XTAL_FREQ_Hz, (div ? div : 256));
+ }
+ }
+ break;
+ }
+ } else {
+ /* Dependent on crystal clock */
+ freq = chipcHw_XTAL_FREQ_Hz;
+ }
+
+ div = *pClockCtrl & chipcHw_REG_DIV_CLOCK_DIV_MASK;
+ return chipcHw_divide(freq, (div ? div : 256));
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Set clock fequency for miscellaneous configurable clocks
+*
+* This function sets clock frequency
+*
+* @return Configured clock frequency in Hz
+*
+*/
+/****************************************************************************/
+chipcHw_freq chipcHw_setClockFrequency(chipcHw_CLOCK_e clock, /* [ IN ] Configurable clock */
+ uint32_t freq /* [ IN ] Clock frequency in Hz */
+ ) {
+ volatile uint32_t *pPLLReg = (uint32_t *) 0x0;
+ volatile uint32_t *pClockCtrl = (uint32_t *) 0x0;
+ volatile uint32_t *pDependentClock = (uint32_t *) 0x0;
+ uint32_t vcoFreqPll1Hz = 0; /* Effective VCO frequency for PLL1 in Hz */
+ uint32_t desVcoFreqPll1Hz = 0; /* Desired VCO frequency for PLL1 in Hz */
+ uint32_t vcoFreqPll2Hz = 0; /* Effective VCO frequency for PLL2 in Hz */
+ uint32_t dependentClockType = 0;
+ uint32_t vcoHz = 0;
+ uint32_t desVcoHz = 0;
+
+ /* Get VCO frequencies */
+ if ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MASK) != chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_INTEGER) {
+ uint64_t adjustFreq = 0;
+
+ vcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
+ chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
+ ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
+
+ /* Adjusted frequency due to chipcHw_REG_PLL_DIVIDER_NDIV_f_SS */
+ adjustFreq = (uint64_t) chipcHw_XTAL_FREQ_Hz *
+ (uint64_t) chipcHw_REG_PLL_DIVIDER_NDIV_f_SS *
+ chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, (chipcHw_REG_PLL_PREDIVIDER_P2 * (uint64_t) chipcHw_REG_PLL_DIVIDER_FRAC));
+ vcoFreqPll1Hz += (uint32_t) adjustFreq;
+
+ /* Desired VCO frequency */
+ desVcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
+ chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
+ (((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT) + 1);
+ } else {
+ vcoFreqPll1Hz = desVcoFreqPll1Hz = chipcHw_XTAL_FREQ_Hz *
+ chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
+ ((pChipcHw->PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
+ }
+ vcoFreqPll2Hz = chipcHw_XTAL_FREQ_Hz * chipcHw_divide(chipcHw_REG_PLL_PREDIVIDER_P1, chipcHw_REG_PLL_PREDIVIDER_P2) *
+ ((pChipcHw->PLLPreDivider2 & chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK) >>
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT);
+
+ switch (clock) {
+ case chipcHw_CLOCK_DDR:
+ /* Configure the DDR_ctrl:BUS ratio settings */
+ {
+ REG_LOCAL_IRQ_SAVE;
+ /* Dvide DDR_phy by two to obtain DDR_ctrl clock */
+ pChipcHw->DDRClock = (pChipcHw->DDRClock & ~chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_MASK) | ((((freq / 2) / chipcHw_getClockFrequency(chipcHw_CLOCK_BUS)) - 1)
+ << chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_SHIFT);
+ REG_LOCAL_IRQ_RESTORE;
+ }
+ pPLLReg = &pChipcHw->DDRClock;
+ vcoHz = vcoFreqPll1Hz;
+ desVcoHz = desVcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_ARM:
+ pPLLReg = &pChipcHw->ARMClock;
+ vcoHz = vcoFreqPll1Hz;
+ desVcoHz = desVcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_ESW:
+ pPLLReg = &pChipcHw->ESWClock;
+ vcoHz = vcoFreqPll1Hz;
+ desVcoHz = desVcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_VPM:
+ /* Configure the VPM:BUS ratio settings */
+ {
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->VPMClock = (pChipcHw->VPMClock & ~chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_MASK) | ((chipcHw_divide (freq, chipcHw_getClockFrequency(chipcHw_CLOCK_BUS)) - 1)
+ << chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_SHIFT);
+ REG_LOCAL_IRQ_RESTORE;
+ }
+ pPLLReg = &pChipcHw->VPMClock;
+ vcoHz = vcoFreqPll1Hz;
+ desVcoHz = desVcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_ESW125:
+ pPLLReg = &pChipcHw->ESW125Clock;
+ vcoHz = vcoFreqPll1Hz;
+ desVcoHz = desVcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_UART:
+ pPLLReg = &pChipcHw->UARTClock;
+ vcoHz = vcoFreqPll1Hz;
+ desVcoHz = desVcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_SDIO0:
+ pPLLReg = &pChipcHw->SDIO0Clock;
+ vcoHz = vcoFreqPll1Hz;
+ desVcoHz = desVcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_SDIO1:
+ pPLLReg = &pChipcHw->SDIO1Clock;
+ vcoHz = vcoFreqPll1Hz;
+ desVcoHz = desVcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_SPI:
+ pPLLReg = &pChipcHw->SPIClock;
+ vcoHz = vcoFreqPll1Hz;
+ desVcoHz = desVcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_ETM:
+ pPLLReg = &pChipcHw->ETMClock;
+ vcoHz = vcoFreqPll1Hz;
+ desVcoHz = desVcoFreqPll1Hz;
+ break;
+ case chipcHw_CLOCK_USB:
+ pPLLReg = &pChipcHw->USBClock;
+ vcoHz = vcoFreqPll2Hz;
+ desVcoHz = vcoFreqPll2Hz;
+ break;
+ case chipcHw_CLOCK_LCD:
+ pPLLReg = &pChipcHw->LCDClock;
+ vcoHz = vcoFreqPll2Hz;
+ desVcoHz = vcoFreqPll2Hz;
+ break;
+ case chipcHw_CLOCK_APM:
+ pPLLReg = &pChipcHw->APMClock;
+ vcoHz = vcoFreqPll2Hz;
+ desVcoHz = vcoFreqPll2Hz;
+ break;
+ case chipcHw_CLOCK_BUS:
+ pClockCtrl = &pChipcHw->ACLKClock;
+ pDependentClock = &pChipcHw->ARMClock;
+ vcoHz = vcoFreqPll1Hz;
+ desVcoHz = desVcoFreqPll1Hz;
+ dependentClockType = PLL_CLOCK;
+ break;
+ case chipcHw_CLOCK_OTP:
+ pClockCtrl = &pChipcHw->OTPClock;
+ break;
+ case chipcHw_CLOCK_I2C:
+ pClockCtrl = &pChipcHw->I2CClock;
+ break;
+ case chipcHw_CLOCK_I2S0:
+ pClockCtrl = &pChipcHw->I2S0Clock;
+ break;
+ case chipcHw_CLOCK_RTBUS:
+ pClockCtrl = &pChipcHw->RTBUSClock;
+ pDependentClock = &pChipcHw->ACLKClock;
+ dependentClockType = NON_PLL_CLOCK;
+ break;
+ case chipcHw_CLOCK_APM100:
+ pClockCtrl = &pChipcHw->APM100Clock;
+ pDependentClock = &pChipcHw->APMClock;
+ vcoHz = vcoFreqPll2Hz;
+ desVcoHz = vcoFreqPll2Hz;
+ dependentClockType = PLL_CLOCK;
+ break;
+ case chipcHw_CLOCK_TSC:
+ pClockCtrl = &pChipcHw->TSCClock;
+ break;
+ case chipcHw_CLOCK_LED:
+ pClockCtrl = &pChipcHw->LEDClock;
+ break;
+ case chipcHw_CLOCK_I2S1:
+ pClockCtrl = &pChipcHw->I2S1Clock;
+ break;
+ }
+
+ if (pPLLReg) {
+ /* Select XTAL as bypass source */
+ reg32_modify_and(pPLLReg, ~chipcHw_REG_PLL_CLOCK_SOURCE_GPIO);
+ reg32_modify_or(pPLLReg, chipcHw_REG_PLL_CLOCK_BYPASS_SELECT);
+ /* For DDR settings use only the PLL divider clock */
+ if (pPLLReg == &pChipcHw->DDRClock) {
+ /* Set M1DIV for PLL1, which controls the DDR clock */
+ reg32_write(&pChipcHw->PLLDivider, (pChipcHw->PLLDivider & 0x00FFFFFF) | ((chipcHw_REG_PLL_DIVIDER_MDIV (desVcoHz, freq)) << 24));
+ /* Calculate expected frequency */
+ freq = chipcHw_divide(vcoHz, (((pChipcHw->PLLDivider & 0xFF000000) >> 24) ? ((pChipcHw->PLLDivider & 0xFF000000) >> 24) : 256));
+ } else {
+ /* From chip revision number B0, LCD clock is internally divided by 2 */
+ if ((pPLLReg == &pChipcHw->LCDClock) && (chipcHw_getChipRevisionNumber() != chipcHw_REV_NUMBER_A0)) {
+ desVcoHz >>= 1;
+ vcoHz >>= 1;
+ }
+ /* Set MDIV to change the frequency */
+ reg32_modify_and(pPLLReg, ~(chipcHw_REG_PLL_CLOCK_MDIV_MASK));
+ reg32_modify_or(pPLLReg, chipcHw_REG_PLL_DIVIDER_MDIV(desVcoHz, freq));
+ /* Calculate expected frequency */
+ freq = chipcHw_divide(vcoHz, ((*(pPLLReg) & chipcHw_REG_PLL_CLOCK_MDIV_MASK) ? (*(pPLLReg) & chipcHw_REG_PLL_CLOCK_MDIV_MASK) : 256));
+ }
+ /* Wait for for atleast 200ns as per the protocol to change frequency */
+ udelay(1);
+ /* Do not bypass */
+ reg32_modify_and(pPLLReg, ~chipcHw_REG_PLL_CLOCK_BYPASS_SELECT);
+ /* Return the configured frequency */
+ return freq;
+ } else if (pClockCtrl) {
+ uint32_t divider = 0;
+
+ /* Divider clock should not be bypassed */
+ reg32_modify_and(pClockCtrl,
+ ~chipcHw_REG_DIV_CLOCK_BYPASS_SELECT);
+
+ /* Identify the clock source */
+ if (pDependentClock) {
+ switch (dependentClockType) {
+ case PLL_CLOCK:
+ divider = chipcHw_divide(chipcHw_divide (desVcoHz, (*pDependentClock & chipcHw_REG_PLL_CLOCK_MDIV_MASK)), freq);
+ break;
+ case NON_PLL_CLOCK:
+ {
+ uint32_t sourceClock = 0;
+
+ if (pDependentClock == (uint32_t *) &pChipcHw->ACLKClock) {
+ sourceClock = chipcHw_getClockFrequency (chipcHw_CLOCK_BUS);
+ } else {
+ uint32_t div = *pDependentClock & chipcHw_REG_DIV_CLOCK_DIV_MASK;
+ sourceClock = chipcHw_divide (chipcHw_XTAL_FREQ_Hz, ((div) ? div : 256));
+ }
+ divider = chipcHw_divide(sourceClock, freq);
+ }
+ break;
+ }
+ } else {
+ divider = chipcHw_divide(chipcHw_XTAL_FREQ_Hz, freq);
+ }
+
+ if (divider) {
+ REG_LOCAL_IRQ_SAVE;
+ /* Set the divider to obtain the required frequency */
+ *pClockCtrl = (*pClockCtrl & (~chipcHw_REG_DIV_CLOCK_DIV_MASK)) | (((divider > 256) ? chipcHw_REG_DIV_CLOCK_DIV_256 : divider) & chipcHw_REG_DIV_CLOCK_DIV_MASK);
+ REG_LOCAL_IRQ_RESTORE;
+ return freq;
+ }
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(chipcHw_setClockFrequency);
+
+/****************************************************************************/
+/**
+* @brief Set VPM clock in sync with BUS clock for Chip Rev #A0
+*
+* This function does the phase adjustment between VPM and BUS clock
+*
+* @return >= 0 : On success (# of adjustment required)
+* -1 : On failure
+*
+*/
+/****************************************************************************/
+static int vpmPhaseAlignA0(void)
+{
+ uint32_t phaseControl;
+ uint32_t phaseValue;
+ uint32_t prevPhaseComp;
+ int iter = 0;
+ int adjustCount = 0;
+ int count = 0;
+
+ for (iter = 0; (iter < MAX_PHASE_ALIGN_ATTEMPTS) && (adjustCount < MAX_PHASE_ADJUST_COUNT); iter++) {
+ phaseControl = (pChipcHw->VPMClock & chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK) >> chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT;
+ phaseValue = 0;
+ prevPhaseComp = 0;
+
+ /* Step 1: Look for falling PH_COMP transition */
+
+ /* Read the contents of VPM Clock resgister */
+ phaseValue = pChipcHw->VPMClock;
+ do {
+ /* Store previous value of phase comparator */
+ prevPhaseComp = phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP;
+ /* Change the value of PH_CTRL. */
+ reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
+ /* Wait atleast 20 ns */
+ udelay(1);
+ /* Toggle the LOAD_CH after phase control is written. */
+ pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
+ /* Read the contents of VPM Clock resgister. */
+ phaseValue = pChipcHw->VPMClock;
+
+ if ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0x0) {
+ phaseControl = (0x3F & (phaseControl - 1));
+ } else {
+ /* Increment to the Phase count value for next write, if Phase is not stable. */
+ phaseControl = (0x3F & (phaseControl + 1));
+ }
+ /* Count number of adjustment made */
+ adjustCount++;
+ } while (((prevPhaseComp == (phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP)) || /* Look for a transition */
+ ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) != 0x0)) && /* Look for a falling edge */
+ (adjustCount < MAX_PHASE_ADJUST_COUNT) /* Do not exceed the limit while trying */
+ );
+
+ if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
+ /* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
+ return -1;
+ }
+
+ /* Step 2: Keep moving forward to make sure falling PH_COMP transition was valid */
+
+ for (count = 0; (count < 5) && ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0); count++) {
+ phaseControl = (0x3F & (phaseControl + 1));
+ reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
+ /* Wait atleast 20 ns */
+ udelay(1);
+ /* Toggle the LOAD_CH after phase control is written. */
+ pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
+ phaseValue = pChipcHw->VPMClock;
+ /* Count number of adjustment made */
+ adjustCount++;
+ }
+
+ if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
+ /* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
+ return -1;
+ }
+
+ if (count != 5) {
+ /* Detected false transition */
+ continue;
+ }
+
+ /* Step 3: Keep moving backward to make sure falling PH_COMP transition was stable */
+
+ for (count = 0; (count < 3) && ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0); count++) {
+ phaseControl = (0x3F & (phaseControl - 1));
+ reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
+ /* Wait atleast 20 ns */
+ udelay(1);
+ /* Toggle the LOAD_CH after phase control is written. */
+ pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
+ phaseValue = pChipcHw->VPMClock;
+ /* Count number of adjustment made */
+ adjustCount++;
+ }
+
+ if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
+ /* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
+ return -1;
+ }
+
+ if (count != 3) {
+ /* Detected noisy transition */
+ continue;
+ }
+
+ /* Step 4: Keep moving backward before the original transition took place. */
+
+ for (count = 0; (count < 5); count++) {
+ phaseControl = (0x3F & (phaseControl - 1));
+ reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
+ /* Wait atleast 20 ns */
+ udelay(1);
+ /* Toggle the LOAD_CH after phase control is written. */
+ pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
+ phaseValue = pChipcHw->VPMClock;
+ /* Count number of adjustment made */
+ adjustCount++;
+ }
+
+ if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
+ /* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
+ return -1;
+ }
+
+ if ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0) {
+ /* Detected false transition */
+ continue;
+ }
+
+ /* Step 5: Re discover the valid transition */
+
+ do {
+ /* Store previous value of phase comparator */
+ prevPhaseComp = phaseValue;
+ /* Change the value of PH_CTRL. */
+ reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
+ /* Wait atleast 20 ns */
+ udelay(1);
+ /* Toggle the LOAD_CH after phase control is written. */
+ pChipcHw->VPMClock ^=
+ chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
+ /* Read the contents of VPM Clock resgister. */
+ phaseValue = pChipcHw->VPMClock;
+
+ if ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) == 0x0) {
+ phaseControl = (0x3F & (phaseControl - 1));
+ } else {
+ /* Increment to the Phase count value for next write, if Phase is not stable. */
+ phaseControl = (0x3F & (phaseControl + 1));
+ }
+
+ /* Count number of adjustment made */
+ adjustCount++;
+ } while (((prevPhaseComp == (phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP)) || ((phaseValue & chipcHw_REG_PLL_CLOCK_PHASE_COMP) != 0x0)) && (adjustCount < MAX_PHASE_ADJUST_COUNT));
+
+ if (adjustCount >= MAX_PHASE_ADJUST_COUNT) {
+ /* Failed to align VPM phase after MAX_PHASE_ADJUST_COUNT tries */
+ return -1;
+ } else {
+ /* Valid phase must have detected */
+ break;
+ }
+ }
+
+ /* For VPM Phase should be perfectly aligned. */
+ phaseControl = (((pChipcHw->VPMClock >> chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT) - 1) & 0x3F);
+ {
+ REG_LOCAL_IRQ_SAVE;
+
+ pChipcHw->VPMClock = (pChipcHw->VPMClock & ~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT);
+ /* Load new phase value */
+ pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
+
+ REG_LOCAL_IRQ_RESTORE;
+ }
+ /* Return the status */
+ return (int)adjustCount;
+}
+
+/****************************************************************************/
+/**
+* @brief Set VPM clock in sync with BUS clock
+*
+* This function does the phase adjustment between VPM and BUS clock
+*
+* @return >= 0 : On success (# of adjustment required)
+* -1 : On failure
+*
+*/
+/****************************************************************************/
+int chipcHw_vpmPhaseAlign(void)
+{
+
+ if (chipcHw_getChipRevisionNumber() == chipcHw_REV_NUMBER_A0) {
+ return vpmPhaseAlignA0();
+ } else {
+ uint32_t phaseControl = chipcHw_getVpmPhaseControl();
+ uint32_t phaseValue = 0;
+ int adjustCount = 0;
+
+ /* Disable VPM access */
+ pChipcHw->Spare1 &= ~chipcHw_REG_SPARE1_VPM_BUS_ACCESS_ENABLE;
+ /* Disable HW VPM phase alignment */
+ chipcHw_vpmHwPhaseAlignDisable();
+ /* Enable SW VPM phase alignment */
+ chipcHw_vpmSwPhaseAlignEnable();
+ /* Adjust VPM phase */
+ while (adjustCount < MAX_PHASE_ADJUST_COUNT) {
+ phaseValue = chipcHw_getVpmHwPhaseAlignStatus();
+
+ /* Adjust phase control value */
+ if (phaseValue > 0xF) {
+ /* Increment phase control value */
+ phaseControl++;
+ } else if (phaseValue < 0xF) {
+ /* Decrement phase control value */
+ phaseControl--;
+ } else {
+ /* Enable VPM access */
+ pChipcHw->Spare1 |= chipcHw_REG_SPARE1_VPM_BUS_ACCESS_ENABLE;
+ /* Return adjust count */
+ return adjustCount;
+ }
+ /* Change the value of PH_CTRL. */
+ reg32_write(&pChipcHw->VPMClock, (pChipcHw->VPMClock & (~chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK)) | (phaseControl << chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT));
+ /* Wait atleast 20 ns */
+ udelay(1);
+ /* Toggle the LOAD_CH after phase control is written. */
+ pChipcHw->VPMClock ^= chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE;
+ /* Count adjustment */
+ adjustCount++;
+ }
+ }
+
+ /* Disable VPM access */
+ pChipcHw->Spare1 &= ~chipcHw_REG_SPARE1_VPM_BUS_ACCESS_ENABLE;
+ return -1;
+}
+
+/****************************************************************************/
+/**
+* @brief Local Divide function
+*
+* This function does the divide
+*
+* @return divide value
+*
+*/
+/****************************************************************************/
+static int chipcHw_divide(int num, int denom)
+{
+ int r;
+ int t = 1;
+
+ /* Shift denom and t up to the largest value to optimize algorithm */
+ /* t contains the units of each divide */
+ while ((denom & 0x40000000) == 0) { /* fails if denom=0 */
+ denom = denom << 1;
+ t = t << 1;
+ }
+
+ /* Intialize the result */
+ r = 0;
+
+ do {
+ /* Determine if there exists a positive remainder */
+ if ((num - denom) >= 0) {
+ /* Accumlate t to the result and calculate a new remainder */
+ num = num - denom;
+ r = r + t;
+ }
+ /* Continue to shift denom and shift t down to 0 */
+ denom = denom >> 1;
+ t = t >> 1;
+ } while (t != 0);
+
+ return r;
+}
diff --git a/arch/arm/mach-bcmring/csp/chipc/chipcHw_init.c b/arch/arm/mach-bcmring/csp/chipc/chipcHw_init.c
new file mode 100644
index 00000000000..367df75d4bb
--- /dev/null
+++ b/arch/arm/mach-bcmring/csp/chipc/chipcHw_init.c
@@ -0,0 +1,293 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file chipcHw_init.c
+*
+* @brief Low level CHIPC PLL configuration functions
+*
+* @note
+*
+* These routines provide basic PLL controlling functionality only.
+*/
+/****************************************************************************/
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <csp/errno.h>
+#include <csp/stdint.h>
+#include <csp/module.h>
+
+#include <mach/csp/chipcHw_def.h>
+#include <mach/csp/chipcHw_inline.h>
+
+#include <csp/reg.h>
+#include <csp/delay.h>
+/* ---- Private Constants and Types --------------------------------------- */
+
+/*
+ Calculation for NDIV_i to obtain VCO frequency
+ -----------------------------------------------
+
+ Freq_vco = Freq_ref * (P2 / P1) * (PLL_NDIV_i + PLL_NDIV_f)
+ for Freq_vco = VCO_FREQ_MHz
+ Freq_ref = chipcHw_XTAL_FREQ_Hz
+ PLL_P1 = PLL_P2 = 1
+ and
+ PLL_NDIV_f = 0
+
+ We get:
+ PLL_NDIV_i = Freq_vco / Freq_ref = VCO_FREQ_MHz / chipcHw_XTAL_FREQ_Hz
+
+ Calculation for PLL MDIV to obtain frequency Freq_x for channel x
+ -----------------------------------------------------------------
+ Freq_x = chipcHw_XTAL_FREQ_Hz * PLL_NDIV_i / PLL_MDIV_x = VCO_FREQ_MHz / PLL_MDIV_x
+
+ PLL_MDIV_x = VCO_FREQ_MHz / Freq_x
+*/
+
+/* ---- Private Variables ------------------------------------------------- */
+/****************************************************************************/
+/**
+* @brief Initializes the PLL2
+*
+* This function initializes the PLL2
+*
+*/
+/****************************************************************************/
+void chipcHw_pll2Enable(uint32_t vcoFreqHz)
+{
+ uint32_t pllPreDivider2 = 0;
+
+ {
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->PLLConfig2 =
+ chipcHw_REG_PLL_CONFIG_D_RESET |
+ chipcHw_REG_PLL_CONFIG_A_RESET;
+
+ pllPreDivider2 = chipcHw_REG_PLL_PREDIVIDER_POWER_DOWN |
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_INTEGER |
+ (chipcHw_REG_PLL_PREDIVIDER_NDIV_i(vcoFreqHz) <<
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT) |
+ (chipcHw_REG_PLL_PREDIVIDER_P1 <<
+ chipcHw_REG_PLL_PREDIVIDER_P1_SHIFT) |
+ (chipcHw_REG_PLL_PREDIVIDER_P2 <<
+ chipcHw_REG_PLL_PREDIVIDER_P2_SHIFT);
+
+ /* Enable CHIPC registers to control the PLL */
+ pChipcHw->PLLStatus |= chipcHw_REG_PLL_STATUS_CONTROL_ENABLE;
+
+ /* Set pre divider to get desired VCO frequency */
+ pChipcHw->PLLPreDivider2 = pllPreDivider2;
+ /* Set NDIV Frac */
+ pChipcHw->PLLDivider2 = chipcHw_REG_PLL_DIVIDER_NDIV_f;
+
+ /* This has to be removed once the default values are fixed for PLL2. */
+ pChipcHw->PLLControl12 = 0x38000700;
+ pChipcHw->PLLControl22 = 0x00000015;
+
+ /* Reset PLL2 */
+ if (vcoFreqHz > chipcHw_REG_PLL_CONFIG_VCO_SPLIT_FREQ) {
+ pChipcHw->PLLConfig2 = chipcHw_REG_PLL_CONFIG_D_RESET |
+ chipcHw_REG_PLL_CONFIG_A_RESET |
+ chipcHw_REG_PLL_CONFIG_VCO_1601_3200 |
+ chipcHw_REG_PLL_CONFIG_POWER_DOWN;
+ } else {
+ pChipcHw->PLLConfig2 = chipcHw_REG_PLL_CONFIG_D_RESET |
+ chipcHw_REG_PLL_CONFIG_A_RESET |
+ chipcHw_REG_PLL_CONFIG_VCO_800_1600 |
+ chipcHw_REG_PLL_CONFIG_POWER_DOWN;
+ }
+ REG_LOCAL_IRQ_RESTORE;
+ }
+
+ /* Insert certain amount of delay before deasserting ARESET. */
+ udelay(1);
+
+ {
+ REG_LOCAL_IRQ_SAVE;
+ /* Remove analog reset and Power on the PLL */
+ pChipcHw->PLLConfig2 &=
+ ~(chipcHw_REG_PLL_CONFIG_A_RESET |
+ chipcHw_REG_PLL_CONFIG_POWER_DOWN);
+
+ REG_LOCAL_IRQ_RESTORE;
+
+ }
+
+ /* Wait until PLL is locked */
+ while (!(pChipcHw->PLLStatus2 & chipcHw_REG_PLL_STATUS_LOCKED))
+ ;
+
+ {
+ REG_LOCAL_IRQ_SAVE;
+ /* Remove digital reset */
+ pChipcHw->PLLConfig2 &= ~chipcHw_REG_PLL_CONFIG_D_RESET;
+
+ REG_LOCAL_IRQ_RESTORE;
+ }
+}
+
+EXPORT_SYMBOL(chipcHw_pll2Enable);
+
+/****************************************************************************/
+/**
+* @brief Initializes the PLL1
+*
+* This function initializes the PLL1
+*
+*/
+/****************************************************************************/
+void chipcHw_pll1Enable(uint32_t vcoFreqHz, chipcHw_SPREAD_SPECTRUM_e ssSupport)
+{
+ uint32_t pllPreDivider = 0;
+
+ {
+ REG_LOCAL_IRQ_SAVE;
+
+ pChipcHw->PLLConfig =
+ chipcHw_REG_PLL_CONFIG_D_RESET |
+ chipcHw_REG_PLL_CONFIG_A_RESET;
+ /* Setting VCO frequency */
+ if (ssSupport == chipcHw_SPREAD_SPECTRUM_ALLOW) {
+ pllPreDivider =
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MASH_1_8 |
+ ((chipcHw_REG_PLL_PREDIVIDER_NDIV_i(vcoFreqHz) -
+ 1) << chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT) |
+ (chipcHw_REG_PLL_PREDIVIDER_P1 <<
+ chipcHw_REG_PLL_PREDIVIDER_P1_SHIFT) |
+ (chipcHw_REG_PLL_PREDIVIDER_P2 <<
+ chipcHw_REG_PLL_PREDIVIDER_P2_SHIFT);
+ } else {
+ pllPreDivider = chipcHw_REG_PLL_PREDIVIDER_POWER_DOWN |
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_INTEGER |
+ (chipcHw_REG_PLL_PREDIVIDER_NDIV_i(vcoFreqHz) <<
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT) |
+ (chipcHw_REG_PLL_PREDIVIDER_P1 <<
+ chipcHw_REG_PLL_PREDIVIDER_P1_SHIFT) |
+ (chipcHw_REG_PLL_PREDIVIDER_P2 <<
+ chipcHw_REG_PLL_PREDIVIDER_P2_SHIFT);
+ }
+
+ /* Enable CHIPC registers to control the PLL */
+ pChipcHw->PLLStatus |= chipcHw_REG_PLL_STATUS_CONTROL_ENABLE;
+
+ /* Set pre divider to get desired VCO frequency */
+ pChipcHw->PLLPreDivider = pllPreDivider;
+ /* Set NDIV Frac */
+ if (ssSupport == chipcHw_SPREAD_SPECTRUM_ALLOW) {
+ pChipcHw->PLLDivider = chipcHw_REG_PLL_DIVIDER_M1DIV |
+ chipcHw_REG_PLL_DIVIDER_NDIV_f_SS;
+ } else {
+ pChipcHw->PLLDivider = chipcHw_REG_PLL_DIVIDER_M1DIV |
+ chipcHw_REG_PLL_DIVIDER_NDIV_f;
+ }
+
+ /* Reset PLL1 */
+ if (vcoFreqHz > chipcHw_REG_PLL_CONFIG_VCO_SPLIT_FREQ) {
+ pChipcHw->PLLConfig = chipcHw_REG_PLL_CONFIG_D_RESET |
+ chipcHw_REG_PLL_CONFIG_A_RESET |
+ chipcHw_REG_PLL_CONFIG_VCO_1601_3200 |
+ chipcHw_REG_PLL_CONFIG_POWER_DOWN;
+ } else {
+ pChipcHw->PLLConfig = chipcHw_REG_PLL_CONFIG_D_RESET |
+ chipcHw_REG_PLL_CONFIG_A_RESET |
+ chipcHw_REG_PLL_CONFIG_VCO_800_1600 |
+ chipcHw_REG_PLL_CONFIG_POWER_DOWN;
+ }
+
+ REG_LOCAL_IRQ_RESTORE;
+
+ /* Insert certain amount of delay before deasserting ARESET. */
+ udelay(1);
+
+ {
+ REG_LOCAL_IRQ_SAVE;
+ /* Remove analog reset and Power on the PLL */
+ pChipcHw->PLLConfig &=
+ ~(chipcHw_REG_PLL_CONFIG_A_RESET |
+ chipcHw_REG_PLL_CONFIG_POWER_DOWN);
+ REG_LOCAL_IRQ_RESTORE;
+ }
+
+ /* Wait until PLL is locked */
+ while (!(pChipcHw->PLLStatus & chipcHw_REG_PLL_STATUS_LOCKED)
+ || !(pChipcHw->
+ PLLStatus2 & chipcHw_REG_PLL_STATUS_LOCKED))
+ ;
+
+ /* Remove digital reset */
+ {
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->PLLConfig &= ~chipcHw_REG_PLL_CONFIG_D_RESET;
+ REG_LOCAL_IRQ_RESTORE;
+ }
+ }
+}
+
+EXPORT_SYMBOL(chipcHw_pll1Enable);
+
+/****************************************************************************/
+/**
+* @brief Initializes the chipc module
+*
+* This function initializes the PLLs and core system clocks
+*
+*/
+/****************************************************************************/
+
+void chipcHw_Init(chipcHw_INIT_PARAM_t *initParam /* [ IN ] Misc chip initialization parameter */
+ ) {
+#if !(defined(__KERNEL__) && !defined(STANDALONE))
+ delay_init();
+#endif
+
+ /* Do not program PLL, when warm reset */
+ if (!(chipcHw_getStickyBits() & chipcHw_REG_STICKY_CHIP_WARM_RESET)) {
+ chipcHw_pll1Enable(initParam->pllVcoFreqHz,
+ initParam->ssSupport);
+ chipcHw_pll2Enable(initParam->pll2VcoFreqHz);
+ } else {
+ /* Clear sticky bits */
+ chipcHw_clearStickyBits(chipcHw_REG_STICKY_CHIP_WARM_RESET);
+ }
+ /* Clear sticky bits */
+ chipcHw_clearStickyBits(chipcHw_REG_STICKY_CHIP_SOFT_RESET);
+
+ /* Before configuring the ARM clock, atleast we need to make sure BUS clock maintains the proper ratio with ARM clock */
+ pChipcHw->ACLKClock =
+ (pChipcHw->
+ ACLKClock & ~chipcHw_REG_ACLKClock_CLK_DIV_MASK) | (initParam->
+ armBusRatio &
+ chipcHw_REG_ACLKClock_CLK_DIV_MASK);
+
+ /* Set various core component frequencies. The order in which this is done is important for some. */
+ /* The RTBUS (DDR PHY) is derived from the BUS, and the BUS from the ARM, and VPM needs to know BUS */
+ /* frequency to find its ratio with the BUS. Hence we must set the ARM first, followed by the BUS, */
+ /* then VPM and RTBUS. */
+
+ chipcHw_setClockFrequency(chipcHw_CLOCK_ARM,
+ initParam->busClockFreqHz *
+ initParam->armBusRatio);
+ chipcHw_setClockFrequency(chipcHw_CLOCK_BUS, initParam->busClockFreqHz);
+ chipcHw_setClockFrequency(chipcHw_CLOCK_VPM,
+ initParam->busClockFreqHz *
+ initParam->vpmBusRatio);
+ chipcHw_setClockFrequency(chipcHw_CLOCK_DDR,
+ initParam->busClockFreqHz *
+ initParam->ddrBusRatio);
+ chipcHw_setClockFrequency(chipcHw_CLOCK_RTBUS,
+ initParam->busClockFreqHz / 2);
+}
diff --git a/arch/arm/mach-bcmring/csp/chipc/chipcHw_reset.c b/arch/arm/mach-bcmring/csp/chipc/chipcHw_reset.c
new file mode 100644
index 00000000000..2671d8896bb
--- /dev/null
+++ b/arch/arm/mach-bcmring/csp/chipc/chipcHw_reset.c
@@ -0,0 +1,124 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/* ---- Include Files ---------------------------------------------------- */
+#include <csp/stdint.h>
+#include <mach/csp/chipcHw_def.h>
+#include <mach/csp/chipcHw_inline.h>
+#include <csp/intcHw.h>
+#include <csp/cache.h>
+
+/* ---- Private Constants and Types --------------------------------------- */
+/* ---- Private Variables ------------------------------------------------- */
+void chipcHw_reset_run_from_aram(void);
+
+typedef void (*RUNFUNC) (void);
+
+/****************************************************************************/
+/**
+* @brief warmReset
+*
+* @note warmReset configures the clocks which are not reset back to the state
+* required to execute on reset. To do so we need to copy the code into internal
+* memory to change the ARM clock while we are not executing from DDR.
+*/
+/****************************************************************************/
+void chipcHw_reset(uint32_t mask)
+{
+ int i = 0;
+ RUNFUNC runFunc = (RUNFUNC) (unsigned long)MM_ADDR_IO_ARAM;
+
+ /* Disable all interrupts */
+ intcHw_irq_disable(INTCHW_INTC0, 0xffffffff);
+ intcHw_irq_disable(INTCHW_INTC1, 0xffffffff);
+ intcHw_irq_disable(INTCHW_SINTC, 0xffffffff);
+
+ {
+ REG_LOCAL_IRQ_SAVE;
+ if (mask & chipcHw_REG_SOFT_RESET_CHIP_SOFT) {
+ chipcHw_softReset(chipcHw_REG_SOFT_RESET_CHIP_SOFT);
+ }
+ /* Bypass the PLL clocks before reboot */
+ pChipcHw->UARTClock |= chipcHw_REG_PLL_CLOCK_BYPASS_SELECT;
+ pChipcHw->SPIClock |= chipcHw_REG_PLL_CLOCK_BYPASS_SELECT;
+
+ /* Copy the chipcHw_warmReset_run_from_aram function into ARAM */
+ do {
+ ((uint32_t *) MM_IO_BASE_ARAM)[i] =
+ ((uint32_t *) &chipcHw_reset_run_from_aram)[i];
+ i++;
+ } while (((uint32_t *) MM_IO_BASE_ARAM)[i - 1] != 0xe1a0f00f); /* 0xe1a0f00f == asm ("mov r15, r15"); */
+
+ CSP_CACHE_FLUSH_ALL;
+
+ /* run the function from ARAM */
+ runFunc();
+
+ /* Code will never get here, but include it to balance REG_LOCAL_IRQ_SAVE above */
+ REG_LOCAL_IRQ_RESTORE;
+ }
+}
+
+/* This function must run from internal memory */
+void chipcHw_reset_run_from_aram(void)
+{
+/* Make sure, pipeline is filled with instructions coming from ARAM */
+__asm (" nop \n\t"
+ " nop \n\t"
+#if defined(__KERNEL__) && !defined(STANDALONE)
+ " MRC p15,#0x0,r0,c1,c0,#0 \n\t"
+ " BIC r0,r0,#0xd \n\t"
+ " MCR p15,#0x0,r0,c1,c0,#0 \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+#endif
+ " nop \n\t"
+ " nop \n\t"
+/* Bypass the ARM clock and switch to XTAL clock */
+ " MOV r2,#0x80000000 \n\t"
+ " LDR r3,[r2,#8] \n\t"
+ " ORR r3,r3,#0x20000 \n\t"
+ " STR r3,[r2,#8] \n\t"
+
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+ " nop \n\t"
+/* Issue reset */
+ " MOV r3,#0x2 \n\t"
+ " STR r3,[r2,#0x80] \n\t"
+/* End here */
+ " MOV pc,pc \n\t");
+/* 0xe1a0f00f == asm ("mov r15, r15"); */
+}
diff --git a/arch/arm/mach-bcmring/csp/chipc/chipcHw_str.c b/arch/arm/mach-bcmring/csp/chipc/chipcHw_str.c
new file mode 100644
index 00000000000..54ad964fe94
--- /dev/null
+++ b/arch/arm/mach-bcmring/csp/chipc/chipcHw_str.c
@@ -0,0 +1,64 @@
+/*****************************************************************************
+* Copyright 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+/****************************************************************************/
+/**
+* @file chipcHw_str.c
+*
+* @brief Contains strings which are useful to linux and csp
+*
+* @note
+*/
+/****************************************************************************/
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <mach/csp/chipcHw_inline.h>
+
+/* ---- Private Constants and Types --------------------------------------- */
+
+static const char *gMuxStr[] = {
+ "GPIO", /* 0 */
+ "KeyPad", /* 1 */
+ "I2C-Host", /* 2 */
+ "SPI", /* 3 */
+ "Uart", /* 4 */
+ "LED-Mtx-P", /* 5 */
+ "LED-Mtx-S", /* 6 */
+ "SDIO-0", /* 7 */
+ "SDIO-1", /* 8 */
+ "PCM", /* 9 */
+ "I2S", /* 10 */
+ "ETM", /* 11 */
+ "Debug", /* 12 */
+ "Misc", /* 13 */
+ "0xE", /* 14 */
+ "0xF", /* 15 */
+};
+
+/****************************************************************************/
+/**
+* @brief Retrieves a string representation of the mux setting for a pin.
+*
+* @return Pointer to a character string.
+*/
+/****************************************************************************/
+
+const char *chipcHw_getGpioPinFunctionStr(int pin)
+{
+ if ((pin < 0) || (pin >= chipcHw_GPIO_COUNT)) {
+ return "";
+ }
+
+ return gMuxStr[chipcHw_getGpioPinFunction(pin)];
+}
diff --git a/arch/arm/mach-bcmring/csp/dmac/Makefile b/arch/arm/mach-bcmring/csp/dmac/Makefile
new file mode 100644
index 00000000000..fb1104fe56b
--- /dev/null
+++ b/arch/arm/mach-bcmring/csp/dmac/Makefile
@@ -0,0 +1 @@
+obj-y += dmacHw.o dmacHw_extra.o \ No newline at end of file
diff --git a/arch/arm/mach-bcmring/csp/dmac/dmacHw.c b/arch/arm/mach-bcmring/csp/dmac/dmacHw.c
new file mode 100644
index 00000000000..7b9bac2d79a
--- /dev/null
+++ b/arch/arm/mach-bcmring/csp/dmac/dmacHw.c
@@ -0,0 +1,917 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file dmacHw.c
+*
+* @brief Low level DMA controller driver routines
+*
+* @note
+*
+* These routines provide basic DMA functionality only.
+*/
+/****************************************************************************/
+
+/* ---- Include Files ---------------------------------------------------- */
+#include <csp/stdint.h>
+#include <csp/string.h>
+#include <stddef.h>
+
+#include <csp/dmacHw.h>
+#include <mach/csp/dmacHw_reg.h>
+#include <mach/csp/dmacHw_priv.h>
+#include <mach/csp/chipcHw_inline.h>
+
+/* ---- External Function Prototypes ------------------------------------- */
+
+/* Allocate DMA control blocks */
+dmacHw_CBLK_t dmacHw_gCblk[dmacHw_MAX_CHANNEL_COUNT];
+
+uint32_t dmaChannelCount_0 = dmacHw_MAX_CHANNEL_COUNT / 2;
+uint32_t dmaChannelCount_1 = dmacHw_MAX_CHANNEL_COUNT / 2;
+
+/****************************************************************************/
+/**
+* @brief Get maximum FIFO for a DMA channel
+*
+* @return Maximum allowable FIFO size
+*
+*
+*/
+/****************************************************************************/
+static uint32_t GetFifoSize(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ ) {
+ uint32_t val = 0;
+ dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+ dmacHw_MISC_t *pMiscReg =
+ (dmacHw_MISC_t *) dmacHw_REG_MISC_BASE(pCblk->module);
+
+ switch (pCblk->channel) {
+ case 0:
+ val = (pMiscReg->CompParm2.lo & 0x70000000) >> 28;
+ break;
+ case 1:
+ val = (pMiscReg->CompParm3.hi & 0x70000000) >> 28;
+ break;
+ case 2:
+ val = (pMiscReg->CompParm3.lo & 0x70000000) >> 28;
+ break;
+ case 3:
+ val = (pMiscReg->CompParm4.hi & 0x70000000) >> 28;
+ break;
+ case 4:
+ val = (pMiscReg->CompParm4.lo & 0x70000000) >> 28;
+ break;
+ case 5:
+ val = (pMiscReg->CompParm5.hi & 0x70000000) >> 28;
+ break;
+ case 6:
+ val = (pMiscReg->CompParm5.lo & 0x70000000) >> 28;
+ break;
+ case 7:
+ val = (pMiscReg->CompParm6.hi & 0x70000000) >> 28;
+ break;
+ }
+
+ if (val <= 0x4) {
+ return 8 << val;
+ } else {
+ dmacHw_ASSERT(0);
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Program channel register to initiate transfer
+*
+* @return void
+*
+*
+* @note
+* - Descriptor buffer MUST ALWAYS be flushed before calling this function
+* - This function should also be called from ISR to program the channel with
+* pending descriptors
+*/
+/****************************************************************************/
+void dmacHw_initiateTransfer(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor /* [ IN ] Descriptor buffer */
+ ) {
+ dmacHw_DESC_RING_t *pRing;
+ dmacHw_DESC_t *pProg;
+ dmacHw_CBLK_t *pCblk;
+
+ pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+ pRing = dmacHw_GET_DESC_RING(pDescriptor);
+
+ if (CHANNEL_BUSY(pCblk->module, pCblk->channel)) {
+ /* Not safe yet to program the channel */
+ return;
+ }
+
+ if (pCblk->varDataStarted) {
+ if (pCblk->descUpdated) {
+ pCblk->descUpdated = 0;
+ pProg =
+ (dmacHw_DESC_t *) ((uint32_t)
+ dmacHw_REG_LLP(pCblk->module,
+ pCblk->channel) +
+ pRing->virt2PhyOffset);
+
+ /* Load descriptor if not loaded */
+ if (!(pProg->ctl.hi & dmacHw_REG_CTL_DONE)) {
+ dmacHw_SET_SAR(pCblk->module, pCblk->channel,
+ pProg->sar);
+ dmacHw_SET_DAR(pCblk->module, pCblk->channel,
+ pProg->dar);
+ dmacHw_REG_CTL_LO(pCblk->module,
+ pCblk->channel) =
+ pProg->ctl.lo;
+ dmacHw_REG_CTL_HI(pCblk->module,
+ pCblk->channel) =
+ pProg->ctl.hi;
+ } else if (pProg == (dmacHw_DESC_t *) pRing->pEnd->llp) {
+ /* Return as end descriptor is processed */
+ return;
+ } else {
+ dmacHw_ASSERT(0);
+ }
+ } else {
+ return;
+ }
+ } else {
+ if (pConfig->transferMode == dmacHw_TRANSFER_MODE_PERIODIC) {
+ /* Do not make a single chain, rather process one descriptor at a time */
+ pProg = pRing->pHead;
+ /* Point to the next descriptor for next iteration */
+ dmacHw_NEXT_DESC(pRing, pHead);
+ } else {
+ /* Return if no more pending descriptor */
+ if (pRing->pEnd == NULL) {
+ return;
+ }
+
+ pProg = pRing->pProg;
+ if (pConfig->transferMode ==
+ dmacHw_TRANSFER_MODE_CONTINUOUS) {
+ /* Make sure a complete ring can be formed */
+ dmacHw_ASSERT((dmacHw_DESC_t *) pRing->pEnd->
+ llp == pRing->pProg);
+ /* Make sure pProg pointing to the pHead */
+ dmacHw_ASSERT((dmacHw_DESC_t *) pRing->pProg ==
+ pRing->pHead);
+ /* Make a complete ring */
+ do {
+ pRing->pProg->ctl.lo |=
+ (dmacHw_REG_CTL_LLP_DST_EN |
+ dmacHw_REG_CTL_LLP_SRC_EN);
+ pRing->pProg =
+ (dmacHw_DESC_t *) pRing->pProg->llp;
+ } while (pRing->pProg != pRing->pHead);
+ } else {
+ /* Make a single long chain */
+ while (pRing->pProg != pRing->pEnd) {
+ pRing->pProg->ctl.lo |=
+ (dmacHw_REG_CTL_LLP_DST_EN |
+ dmacHw_REG_CTL_LLP_SRC_EN);
+ pRing->pProg =
+ (dmacHw_DESC_t *) pRing->pProg->llp;
+ }
+ }
+ }
+
+ /* Program the channel registers */
+ dmacHw_SET_SAR(pCblk->module, pCblk->channel, pProg->sar);
+ dmacHw_SET_DAR(pCblk->module, pCblk->channel, pProg->dar);
+ dmacHw_SET_LLP(pCblk->module, pCblk->channel,
+ (uint32_t) pProg - pRing->virt2PhyOffset);
+ dmacHw_REG_CTL_LO(pCblk->module, pCblk->channel) =
+ pProg->ctl.lo;
+ dmacHw_REG_CTL_HI(pCblk->module, pCblk->channel) =
+ pProg->ctl.hi;
+ if (pRing->pEnd) {
+ /* Remember the descriptor to use next */
+ pRing->pProg = (dmacHw_DESC_t *) pRing->pEnd->llp;
+ }
+ /* Indicate no more pending descriptor */
+ pRing->pEnd = (dmacHw_DESC_t *) NULL;
+ }
+ /* Start DMA operation */
+ dmacHw_DMA_START(pCblk->module, pCblk->channel);
+}
+
+/****************************************************************************/
+/**
+* @brief Initializes DMA
+*
+* This function initializes DMA CSP driver
+*
+* @note
+* Must be called before using any DMA channel
+*/
+/****************************************************************************/
+void dmacHw_initDma(void)
+{
+
+ uint32_t i = 0;
+
+ dmaChannelCount_0 = dmacHw_GET_NUM_CHANNEL(0);
+ dmaChannelCount_1 = dmacHw_GET_NUM_CHANNEL(1);
+
+ /* Enable access to the DMA block */
+ chipcHw_busInterfaceClockEnable(chipcHw_REG_BUS_CLOCK_DMAC0);
+ chipcHw_busInterfaceClockEnable(chipcHw_REG_BUS_CLOCK_DMAC1);
+
+ if ((dmaChannelCount_0 + dmaChannelCount_1) > dmacHw_MAX_CHANNEL_COUNT) {
+ dmacHw_ASSERT(0);
+ }
+
+ memset((void *)dmacHw_gCblk, 0,
+ sizeof(dmacHw_CBLK_t) * (dmaChannelCount_0 + dmaChannelCount_1));
+ for (i = 0; i < dmaChannelCount_0; i++) {
+ dmacHw_gCblk[i].module = 0;
+ dmacHw_gCblk[i].channel = i;
+ }
+ for (i = 0; i < dmaChannelCount_1; i++) {
+ dmacHw_gCblk[i + dmaChannelCount_0].module = 1;
+ dmacHw_gCblk[i + dmaChannelCount_0].channel = i;
+ }
+}
+
+/****************************************************************************/
+/**
+* @brief Exit function for DMA
+*
+* This function isolates DMA from the system
+*
+*/
+/****************************************************************************/
+void dmacHw_exitDma(void)
+{
+ /* Disable access to the DMA block */
+ chipcHw_busInterfaceClockDisable(chipcHw_REG_BUS_CLOCK_DMAC0);
+ chipcHw_busInterfaceClockDisable(chipcHw_REG_BUS_CLOCK_DMAC1);
+}
+
+/****************************************************************************/
+/**
+* @brief Gets a handle to a DMA channel
+*
+* This function returns a handle, representing a control block of a particular DMA channel
+*
+* @return -1 - On Failure
+* handle - On Success, representing a channel control block
+*
+* @note
+* None Channel ID must be created using "dmacHw_MAKE_CHANNEL_ID" macro
+*/
+/****************************************************************************/
+dmacHw_HANDLE_t dmacHw_getChannelHandle(dmacHw_ID_t channelId /* [ IN ] DMA Channel Id */
+ ) {
+ int idx;
+
+ switch ((channelId >> 8)) {
+ case 0:
+ dmacHw_ASSERT((channelId & 0xff) < dmaChannelCount_0);
+ idx = (channelId & 0xff);
+ break;
+ case 1:
+ dmacHw_ASSERT((channelId & 0xff) < dmaChannelCount_1);
+ idx = dmaChannelCount_0 + (channelId & 0xff);
+ break;
+ default:
+ dmacHw_ASSERT(0);
+ return (dmacHw_HANDLE_t) -1;
+ }
+
+ return dmacHw_CBLK_TO_HANDLE(&dmacHw_gCblk[idx]);
+}
+
+/****************************************************************************/
+/**
+* @brief Initializes a DMA channel for use
+*
+* This function initializes and resets a DMA channel for use
+*
+* @return -1 - On Failure
+* 0 - On Success
+*
+* @note
+* None
+*/
+/****************************************************************************/
+int dmacHw_initChannel(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ ) {
+ dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+ int module = pCblk->module;
+ int channel = pCblk->channel;
+
+ /* Reinitialize the control block */
+ memset((void *)pCblk, 0, sizeof(dmacHw_CBLK_t));
+ pCblk->module = module;
+ pCblk->channel = channel;
+
+ /* Enable DMA controller */
+ dmacHw_DMA_ENABLE(pCblk->module);
+ /* Reset DMA channel */
+ dmacHw_RESET_CONTROL_LO(pCblk->module, pCblk->channel);
+ dmacHw_RESET_CONTROL_HI(pCblk->module, pCblk->channel);
+ dmacHw_RESET_CONFIG_LO(pCblk->module, pCblk->channel);
+ dmacHw_RESET_CONFIG_HI(pCblk->module, pCblk->channel);
+
+ /* Clear all raw interrupt status */
+ dmacHw_TRAN_INT_CLEAR(pCblk->module, pCblk->channel);
+ dmacHw_BLOCK_INT_CLEAR(pCblk->module, pCblk->channel);
+ dmacHw_ERROR_INT_CLEAR(pCblk->module, pCblk->channel);
+
+ /* Mask event specific interrupts */
+ dmacHw_TRAN_INT_DISABLE(pCblk->module, pCblk->channel);
+ dmacHw_BLOCK_INT_DISABLE(pCblk->module, pCblk->channel);
+ dmacHw_STRAN_INT_DISABLE(pCblk->module, pCblk->channel);
+ dmacHw_DTRAN_INT_DISABLE(pCblk->module, pCblk->channel);
+ dmacHw_ERROR_INT_DISABLE(pCblk->module, pCblk->channel);
+
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Finds amount of memory required to form a descriptor ring
+*
+*
+* @return Number of bytes required to form a descriptor ring
+*
+*
+*/
+/****************************************************************************/
+uint32_t dmacHw_descriptorLen(uint32_t descCnt /* [ IN ] Number of descriptor in the ring */
+ ) {
+ /* Need extra 4 byte to ensure 32 bit alignment */
+ return (descCnt * sizeof(dmacHw_DESC_t)) + sizeof(dmacHw_DESC_RING_t) +
+ sizeof(uint32_t);
+}
+
+/****************************************************************************/
+/**
+* @brief Initializes descriptor ring
+*
+* This function will initializes the descriptor ring of a DMA channel
+*
+*
+* @return -1 - On failure
+* 0 - On success
+* @note
+* - "len" parameter should be obtained from "dmacHw_descriptorLen"
+* - Descriptor buffer MUST be 32 bit aligned and uncached as it is
+* accessed by ARM and DMA
+*/
+/****************************************************************************/
+int dmacHw_initDescriptor(void *pDescriptorVirt, /* [ IN ] Virtual address of uncahced buffer allocated to form descriptor ring */
+ uint32_t descriptorPhyAddr, /* [ IN ] Physical address of pDescriptorVirt (descriptor buffer) */
+ uint32_t len, /* [ IN ] Size of the pBuf */
+ uint32_t num /* [ IN ] Number of descriptor in the ring */
+ ) {
+ uint32_t i;
+ dmacHw_DESC_RING_t *pRing;
+ dmacHw_DESC_t *pDesc;
+
+ /* Check the alignment of the descriptor */
+ if ((uint32_t) pDescriptorVirt & 0x00000003) {
+ dmacHw_ASSERT(0);
+ return -1;
+ }
+
+ /* Check if enough space has been allocated for descriptor ring */
+ if (len < dmacHw_descriptorLen(num)) {
+ return -1;
+ }
+
+ pRing = dmacHw_GET_DESC_RING(pDescriptorVirt);
+ pRing->pHead =
+ (dmacHw_DESC_t *) ((uint32_t) pRing + sizeof(dmacHw_DESC_RING_t));
+ pRing->pFree = pRing->pTail = pRing->pEnd = pRing->pHead;
+ pRing->pProg = dmacHw_DESC_INIT;
+ /* Initialize link item chain, starting from the head */
+ pDesc = pRing->pHead;
+ /* Find the offset between virtual to physical address */
+ pRing->virt2PhyOffset = (uint32_t) pDescriptorVirt - descriptorPhyAddr;
+
+ /* Form the descriptor ring */
+ for (i = 0; i < num - 1; i++) {
+ /* Clear link list item */
+ memset((void *)pDesc, 0, sizeof(dmacHw_DESC_t));
+ /* Point to the next item in the physical address */
+ pDesc->llpPhy = (uint32_t) (pDesc + 1) - pRing->virt2PhyOffset;
+ /* Point to the next item in the virtual address */
+ pDesc->llp = (uint32_t) (pDesc + 1);
+ /* Mark descriptor is ready to use */
+ pDesc->ctl.hi = dmacHw_DESC_FREE;
+ /* Look into next link list item */
+ pDesc++;
+ }
+
+ /* Clear last link list item */
+ memset((void *)pDesc, 0, sizeof(dmacHw_DESC_t));
+ /* Last item pointing to the first item in the
+ physical address to complete the ring */
+ pDesc->llpPhy = (uint32_t) pRing->pHead - pRing->virt2PhyOffset;
+ /* Last item pointing to the first item in the
+ virtual address to complete the ring
+ */
+ pDesc->llp = (uint32_t) pRing->pHead;
+ /* Mark descriptor is ready to use */
+ pDesc->ctl.hi = dmacHw_DESC_FREE;
+ /* Set the number of descriptors in the ring */
+ pRing->num = num;
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Configure DMA channel
+*
+* @return 0 : On success
+* -1 : On failure
+*/
+/****************************************************************************/
+int dmacHw_configChannel(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ dmacHw_CONFIG_t *pConfig /* [ IN ] Configuration settings */
+ ) {
+ dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+ uint32_t cfgHigh = 0;
+ int srcTrSize;
+ int dstTrSize;
+
+ pCblk->varDataStarted = 0;
+ pCblk->userData = NULL;
+
+ /* Configure
+ - Burst transaction when enough data in available in FIFO
+ - AHB Access protection 1
+ - Source and destination peripheral ports
+ */
+ cfgHigh =
+ dmacHw_REG_CFG_HI_FIFO_ENOUGH | dmacHw_REG_CFG_HI_AHB_HPROT_1 |
+ dmacHw_SRC_PERI_INTF(pConfig->
+ srcPeripheralPort) |
+ dmacHw_DST_PERI_INTF(pConfig->dstPeripheralPort);
+ /* Set priority */
+ dmacHw_SET_CHANNEL_PRIORITY(pCblk->module, pCblk->channel,
+ pConfig->channelPriority);
+
+ if (pConfig->dstStatusRegisterAddress != 0) {
+ /* Destination status update enable */
+ cfgHigh |= dmacHw_REG_CFG_HI_UPDATE_DST_STAT;
+ /* Configure status registers */
+ dmacHw_SET_DSTATAR(pCblk->module, pCblk->channel,
+ pConfig->dstStatusRegisterAddress);
+ }
+
+ if (pConfig->srcStatusRegisterAddress != 0) {
+ /* Source status update enable */
+ cfgHigh |= dmacHw_REG_CFG_HI_UPDATE_SRC_STAT;
+ /* Source status update enable */
+ dmacHw_SET_SSTATAR(pCblk->module, pCblk->channel,
+ pConfig->srcStatusRegisterAddress);
+ }
+ /* Configure the config high register */
+ dmacHw_GET_CONFIG_HI(pCblk->module, pCblk->channel) = cfgHigh;
+
+ /* Clear all raw interrupt status */
+ dmacHw_TRAN_INT_CLEAR(pCblk->module, pCblk->channel);
+ dmacHw_BLOCK_INT_CLEAR(pCblk->module, pCblk->channel);
+ dmacHw_ERROR_INT_CLEAR(pCblk->module, pCblk->channel);
+
+ /* Configure block interrupt */
+ if (pConfig->blockTransferInterrupt == dmacHw_INTERRUPT_ENABLE) {
+ dmacHw_BLOCK_INT_ENABLE(pCblk->module, pCblk->channel);
+ } else {
+ dmacHw_BLOCK_INT_DISABLE(pCblk->module, pCblk->channel);
+ }
+ /* Configure complete transfer interrupt */
+ if (pConfig->completeTransferInterrupt == dmacHw_INTERRUPT_ENABLE) {
+ dmacHw_TRAN_INT_ENABLE(pCblk->module, pCblk->channel);
+ } else {
+ dmacHw_TRAN_INT_DISABLE(pCblk->module, pCblk->channel);
+ }
+ /* Configure error interrupt */
+ if (pConfig->errorInterrupt == dmacHw_INTERRUPT_ENABLE) {
+ dmacHw_ERROR_INT_ENABLE(pCblk->module, pCblk->channel);
+ } else {
+ dmacHw_ERROR_INT_DISABLE(pCblk->module, pCblk->channel);
+ }
+ /* Configure gather register */
+ if (pConfig->srcGatherWidth) {
+ srcTrSize =
+ dmacHw_GetTrWidthInBytes(pConfig->srcMaxTransactionWidth);
+ if (!
+ ((pConfig->srcGatherWidth % srcTrSize)
+ && (pConfig->srcGatherJump % srcTrSize))) {
+ dmacHw_REG_SGR_LO(pCblk->module, pCblk->channel) =
+ ((pConfig->srcGatherWidth /
+ srcTrSize) << 20) | (pConfig->srcGatherJump /
+ srcTrSize);
+ } else {
+ return -1;
+ }
+ }
+ /* Configure scatter register */
+ if (pConfig->dstScatterWidth) {
+ dstTrSize =
+ dmacHw_GetTrWidthInBytes(pConfig->dstMaxTransactionWidth);
+ if (!
+ ((pConfig->dstScatterWidth % dstTrSize)
+ && (pConfig->dstScatterJump % dstTrSize))) {
+ dmacHw_REG_DSR_LO(pCblk->module, pCblk->channel) =
+ ((pConfig->dstScatterWidth /
+ dstTrSize) << 20) | (pConfig->dstScatterJump /
+ dstTrSize);
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Indicates whether DMA transfer is in progress or completed
+*
+* @return DMA transfer status
+* dmacHw_TRANSFER_STATUS_BUSY: DMA Transfer ongoing
+* dmacHw_TRANSFER_STATUS_DONE: DMA Transfer completed
+* dmacHw_TRANSFER_STATUS_ERROR: DMA Transfer error
+*
+*/
+/****************************************************************************/
+dmacHw_TRANSFER_STATUS_e dmacHw_transferCompleted(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ ) {
+ dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+
+ if (CHANNEL_BUSY(pCblk->module, pCblk->channel)) {
+ return dmacHw_TRANSFER_STATUS_BUSY;
+ } else if (dmacHw_REG_INT_RAW_ERROR(pCblk->module) &
+ (0x00000001 << pCblk->channel)) {
+ return dmacHw_TRANSFER_STATUS_ERROR;
+ }
+
+ return dmacHw_TRANSFER_STATUS_DONE;
+}
+
+/****************************************************************************/
+/**
+* @brief Set descriptors for known data length
+*
+* When DMA has to work as a flow controller, this function prepares the
+* descriptor chain to transfer data
+*
+* from:
+* - Memory to memory
+* - Peripheral to memory
+* - Memory to Peripheral
+* - Peripheral to Peripheral
+*
+* @return -1 - On failure
+* 0 - On success
+*
+*/
+/****************************************************************************/
+int dmacHw_setDataDescriptor(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ void *pSrcAddr, /* [ IN ] Source (Peripheral/Memory) address */
+ void *pDstAddr, /* [ IN ] Destination (Peripheral/Memory) address */
+ size_t dataLen /* [ IN ] Data length in bytes */
+ ) {
+ dmacHw_TRANSACTION_WIDTH_e dstTrWidth;
+ dmacHw_TRANSACTION_WIDTH_e srcTrWidth;
+ dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor);
+ dmacHw_DESC_t *pStart;
+ dmacHw_DESC_t *pProg;
+ int srcTs = 0;
+ int blkTs = 0;
+ int oddSize = 0;
+ int descCount = 0;
+ int count = 0;
+ int dstTrSize = 0;
+ int srcTrSize = 0;
+ uint32_t maxBlockSize = dmacHw_MAX_BLOCKSIZE;
+
+ dstTrSize = dmacHw_GetTrWidthInBytes(pConfig->dstMaxTransactionWidth);
+ srcTrSize = dmacHw_GetTrWidthInBytes(pConfig->srcMaxTransactionWidth);
+
+ /* Skip Tx if buffer is NULL or length is unknown */
+ if ((pSrcAddr == NULL) || (pDstAddr == NULL) || (dataLen == 0)) {
+ /* Do not initiate transfer */
+ return -1;
+ }
+
+ /* Ensure scatter and gather are transaction aligned */
+ if ((pConfig->srcGatherWidth % srcTrSize)
+ || (pConfig->dstScatterWidth % dstTrSize)) {
+ return -2;
+ }
+
+ /*
+ Background 1: DMAC can not perform DMA if source and destination addresses are
+ not properly aligned with the channel's transaction width. So, for successful
+ DMA transfer, transaction width must be set according to the alignment of the
+ source and destination address.
+ */
+
+ /* Adjust destination transaction width if destination address is not aligned properly */
+ dstTrWidth = pConfig->dstMaxTransactionWidth;
+ while (dmacHw_ADDRESS_MASK(dstTrSize) & (uint32_t) pDstAddr) {
+ dstTrWidth = dmacHw_GetNextTrWidth(dstTrWidth);
+ dstTrSize = dmacHw_GetTrWidthInBytes(dstTrWidth);
+ }
+
+ /* Adjust source transaction width if source address is not aligned properly */
+ srcTrWidth = pConfig->srcMaxTransactionWidth;
+ while (dmacHw_ADDRESS_MASK(srcTrSize) & (uint32_t) pSrcAddr) {
+ srcTrWidth = dmacHw_GetNextTrWidth(srcTrWidth);
+ srcTrSize = dmacHw_GetTrWidthInBytes(srcTrWidth);
+ }
+
+ /* Find the maximum transaction per descriptor */
+ if (pConfig->maxDataPerBlock
+ && ((pConfig->maxDataPerBlock / srcTrSize) <
+ dmacHw_MAX_BLOCKSIZE)) {
+ maxBlockSize = pConfig->maxDataPerBlock / srcTrSize;
+ }
+
+ /* Find number of source transactions needed to complete the DMA transfer */
+ srcTs = dataLen / srcTrSize;
+ /* Find the odd number of bytes that need to be transferred as single byte transaction width */
+ if (srcTs && (dstTrSize > srcTrSize)) {
+ oddSize = dataLen % dstTrSize;
+ /* Adjust source transaction count due to "oddSize" */
+ srcTs = srcTs - (oddSize / srcTrSize);
+ } else {
+ oddSize = dataLen % srcTrSize;
+ }
+ /* Adjust "descCount" due to "oddSize" */
+ if (oddSize) {
+ descCount++;
+ }
+ /* Find the number of descriptor needed for total "srcTs" */
+ if (srcTs) {
+ descCount += ((srcTs - 1) / maxBlockSize) + 1;
+ }
+
+ /* Check the availability of "descCount" discriptors in the ring */
+ pProg = pRing->pHead;
+ for (count = 0; (descCount <= pRing->num) && (count < descCount);
+ count++) {
+ if ((pProg->ctl.hi & dmacHw_DESC_FREE) == 0) {
+ /* Sufficient descriptors are not available */
+ return -3;
+ }
+ pProg = (dmacHw_DESC_t *) pProg->llp;
+ }
+
+ /* Remember the link list item to program the channel registers */
+ pStart = pProg = pRing->pHead;
+ /* Make a link list with "descCount(=count)" number of descriptors */
+ while (count) {
+ /* Reset channel control information */
+ pProg->ctl.lo = 0;
+ /* Enable source gather if configured */
+ if (pConfig->srcGatherWidth) {
+ pProg->ctl.lo |= dmacHw_REG_CTL_SG_ENABLE;
+ }
+ /* Enable destination scatter if configured */
+ if (pConfig->dstScatterWidth) {
+ pProg->ctl.lo |= dmacHw_REG_CTL_DS_ENABLE;
+ }
+ /* Set source and destination address */
+ pProg->sar = (uint32_t) pSrcAddr;
+ pProg->dar = (uint32_t) pDstAddr;
+ /* Use "devCtl" to mark that user memory need to be freed later if needed */
+ if (pProg == pRing->pHead) {
+ pProg->devCtl = dmacHw_FREE_USER_MEMORY;
+ } else {
+ pProg->devCtl = 0;
+ }
+
+ blkTs = srcTs;
+
+ /* Special treatmeant for last descriptor */
+ if (count == 1) {
+ /* Mark the last descriptor */
+ pProg->ctl.lo &=
+ ~(dmacHw_REG_CTL_LLP_DST_EN |
+ dmacHw_REG_CTL_LLP_SRC_EN);
+ /* Treatment for odd data bytes */
+ if (oddSize) {
+ /* Adjust for single byte transaction width */
+ switch (pConfig->transferType) {
+ case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:
+ dstTrWidth =
+ dmacHw_DST_TRANSACTION_WIDTH_8;
+ blkTs =
+ (oddSize / srcTrSize) +
+ ((oddSize % srcTrSize) ? 1 : 0);
+ break;
+ case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:
+ srcTrWidth =
+ dmacHw_SRC_TRANSACTION_WIDTH_8;
+ blkTs = oddSize;
+ break;
+ case dmacHw_TRANSFER_TYPE_MEM_TO_MEM:
+ srcTrWidth =
+ dmacHw_SRC_TRANSACTION_WIDTH_8;
+ dstTrWidth =
+ dmacHw_DST_TRANSACTION_WIDTH_8;
+ blkTs = oddSize;
+ break;
+ case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL:
+ /* Do not adjust the transaction width */
+ break;
+ }
+ } else {
+ srcTs -= blkTs;
+ }
+ } else {
+ if (srcTs / maxBlockSize) {
+ blkTs = maxBlockSize;
+ }
+ /* Remaining source transactions for next iteration */
+ srcTs -= blkTs;
+ }
+ /* Must have a valid source transactions */
+ dmacHw_ASSERT(blkTs > 0);
+ /* Set control information */
+ if (pConfig->flowControler == dmacHw_FLOW_CONTROL_DMA) {
+ pProg->ctl.lo |= pConfig->transferType |
+ pConfig->srcUpdate |
+ pConfig->dstUpdate |
+ srcTrWidth |
+ dstTrWidth |
+ pConfig->srcMaxBurstWidth |
+ pConfig->dstMaxBurstWidth |
+ pConfig->srcMasterInterface |
+ pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN;
+ } else {
+ uint32_t transferType = 0;
+ switch (pConfig->transferType) {
+ case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:
+ transferType = dmacHw_REG_CTL_TTFC_PM_PERI;
+ break;
+ case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:
+ transferType = dmacHw_REG_CTL_TTFC_MP_PERI;
+ break;
+ default:
+ dmacHw_ASSERT(0);
+ }
+ pProg->ctl.lo |= transferType |
+ pConfig->srcUpdate |
+ pConfig->dstUpdate |
+ srcTrWidth |
+ dstTrWidth |
+ pConfig->srcMaxBurstWidth |
+ pConfig->dstMaxBurstWidth |
+ pConfig->srcMasterInterface |
+ pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN;
+ }
+
+ /* Set block transaction size */
+ pProg->ctl.hi = blkTs & dmacHw_REG_CTL_BLOCK_TS_MASK;
+ /* Look for next descriptor */
+ if (count > 1) {
+ /* Point to the next descriptor */
+ pProg = (dmacHw_DESC_t *) pProg->llp;
+
+ /* Update source and destination address for next iteration */
+ switch (pConfig->transferType) {
+ case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:
+ if (pConfig->dstScatterWidth) {
+ pDstAddr =
+ (char *)pDstAddr +
+ blkTs * srcTrSize +
+ (((blkTs * srcTrSize) /
+ pConfig->dstScatterWidth) *
+ pConfig->dstScatterJump);
+ } else {
+ pDstAddr =
+ (char *)pDstAddr +
+ blkTs * srcTrSize;
+ }
+ break;
+ case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:
+ if (pConfig->srcGatherWidth) {
+ pSrcAddr =
+ (char *)pDstAddr +
+ blkTs * srcTrSize +
+ (((blkTs * srcTrSize) /
+ pConfig->srcGatherWidth) *
+ pConfig->srcGatherJump);
+ } else {
+ pSrcAddr =
+ (char *)pSrcAddr +
+ blkTs * srcTrSize;
+ }
+ break;
+ case dmacHw_TRANSFER_TYPE_MEM_TO_MEM:
+ if (pConfig->dstScatterWidth) {
+ pDstAddr =
+ (char *)pDstAddr +
+ blkTs * srcTrSize +
+ (((blkTs * srcTrSize) /
+ pConfig->dstScatterWidth) *
+ pConfig->dstScatterJump);
+ } else {
+ pDstAddr =
+ (char *)pDstAddr +
+ blkTs * srcTrSize;
+ }
+
+ if (pConfig->srcGatherWidth) {
+ pSrcAddr =
+ (char *)pDstAddr +
+ blkTs * srcTrSize +
+ (((blkTs * srcTrSize) /
+ pConfig->srcGatherWidth) *
+ pConfig->srcGatherJump);
+ } else {
+ pSrcAddr =
+ (char *)pSrcAddr +
+ blkTs * srcTrSize;
+ }
+ break;
+ case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL:
+ /* Do not adjust the address */
+ break;
+ default:
+ dmacHw_ASSERT(0);
+ }
+ } else {
+ /* At the end of transfer "srcTs" must be zero */
+ dmacHw_ASSERT(srcTs == 0);
+ }
+ count--;
+ }
+
+ /* Remember the descriptor to initialize the registers */
+ if (pRing->pProg == dmacHw_DESC_INIT) {
+ pRing->pProg = pStart;
+ }
+ /* Indicate that the descriptor is updated */
+ pRing->pEnd = pProg;
+ /* Head pointing to the next descriptor */
+ pRing->pHead = (dmacHw_DESC_t *) pProg->llp;
+ /* Update Tail pointer if destination is a peripheral,
+ because no one is going to read from the pTail
+ */
+ if (!dmacHw_DST_IS_MEMORY(pConfig->transferType)) {
+ pRing->pTail = pRing->pHead;
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Provides DMA controller attributes
+*
+*
+* @return DMA controller attributes
+*
+* @note
+* None
+*/
+/****************************************************************************/
+uint32_t dmacHw_getDmaControllerAttribute(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ dmacHw_CONTROLLER_ATTRIB_e attr /* [ IN ] DMA Controler attribute of type dmacHw_CONTROLLER_ATTRIB_e */
+ ) {
+ dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+
+ switch (attr) {
+ case dmacHw_CONTROLLER_ATTRIB_CHANNEL_NUM:
+ return dmacHw_GET_NUM_CHANNEL(pCblk->module);
+ case dmacHw_CONTROLLER_ATTRIB_CHANNEL_MAX_BLOCK_SIZE:
+ return (1 <<
+ (dmacHw_GET_MAX_BLOCK_SIZE
+ (pCblk->module, pCblk->module) + 2)) - 8;
+ case dmacHw_CONTROLLER_ATTRIB_MASTER_INTF_NUM:
+ return dmacHw_GET_NUM_INTERFACE(pCblk->module);
+ case dmacHw_CONTROLLER_ATTRIB_CHANNEL_BUS_WIDTH:
+ return 32 << dmacHw_GET_CHANNEL_DATA_WIDTH(pCblk->module,
+ pCblk->channel);
+ case dmacHw_CONTROLLER_ATTRIB_CHANNEL_FIFO_SIZE:
+ return GetFifoSize(handle);
+ }
+ dmacHw_ASSERT(0);
+ return 0;
+}
diff --git a/arch/arm/mach-bcmring/csp/dmac/dmacHw_extra.c b/arch/arm/mach-bcmring/csp/dmac/dmacHw_extra.c
new file mode 100644
index 00000000000..ff7b436d093
--- /dev/null
+++ b/arch/arm/mach-bcmring/csp/dmac/dmacHw_extra.c
@@ -0,0 +1,1017 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file dmacHw_extra.c
+*
+* @brief Extra Low level DMA controller driver routines
+*
+* @note
+*
+* These routines provide basic DMA functionality only.
+*/
+/****************************************************************************/
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <csp/stdint.h>
+#include <stddef.h>
+
+#include <csp/dmacHw.h>
+#include <mach/csp/dmacHw_reg.h>
+#include <mach/csp/dmacHw_priv.h>
+
+extern dmacHw_CBLK_t dmacHw_gCblk[dmacHw_MAX_CHANNEL_COUNT]; /* Declared in dmacHw.c */
+
+/* ---- External Function Prototypes ------------------------------------- */
+
+/* ---- Internal Use Function Prototypes --------------------------------- */
+/****************************************************************************/
+/**
+* @brief Overwrites data length in the descriptor
+*
+* This function overwrites data length in the descriptor
+*
+*
+* @return void
+*
+* @note
+* This is only used for PCM channel
+*/
+/****************************************************************************/
+void dmacHw_setDataLength(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ size_t dataLen /* [ IN ] Data length in bytes */
+ );
+
+/****************************************************************************/
+/**
+* @brief Helper function to display DMA registers
+*
+* @return void
+*
+*
+* @note
+* None
+*/
+/****************************************************************************/
+static void DisplayRegisterContents(int module, /* [ IN ] DMA Controller unit (0-1) */
+ int channel, /* [ IN ] DMA Channel (0-7) / -1(all) */
+ int (*fpPrint) (const char *, ...) /* [ IN ] Callback to the print function */
+ ) {
+ int chan;
+
+ (*fpPrint) ("Displaying register content \n\n");
+ (*fpPrint) ("Module %d: Interrupt raw transfer 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_RAW_TRAN(module)));
+ (*fpPrint) ("Module %d: Interrupt raw block 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_RAW_BLOCK(module)));
+ (*fpPrint) ("Module %d: Interrupt raw src transfer 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_RAW_STRAN(module)));
+ (*fpPrint) ("Module %d: Interrupt raw dst transfer 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_RAW_DTRAN(module)));
+ (*fpPrint) ("Module %d: Interrupt raw error 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_RAW_ERROR(module)));
+ (*fpPrint) ("--------------------------------------------------\n");
+ (*fpPrint) ("Module %d: Interrupt stat transfer 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_STAT_TRAN(module)));
+ (*fpPrint) ("Module %d: Interrupt stat block 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_STAT_BLOCK(module)));
+ (*fpPrint) ("Module %d: Interrupt stat src transfer 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_STAT_STRAN(module)));
+ (*fpPrint) ("Module %d: Interrupt stat dst transfer 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_STAT_DTRAN(module)));
+ (*fpPrint) ("Module %d: Interrupt stat error 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_STAT_ERROR(module)));
+ (*fpPrint) ("--------------------------------------------------\n");
+ (*fpPrint) ("Module %d: Interrupt mask transfer 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_MASK_TRAN(module)));
+ (*fpPrint) ("Module %d: Interrupt mask block 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_MASK_BLOCK(module)));
+ (*fpPrint) ("Module %d: Interrupt mask src transfer 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_MASK_STRAN(module)));
+ (*fpPrint) ("Module %d: Interrupt mask dst transfer 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_MASK_DTRAN(module)));
+ (*fpPrint) ("Module %d: Interrupt mask error 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_MASK_ERROR(module)));
+ (*fpPrint) ("--------------------------------------------------\n");
+ (*fpPrint) ("Module %d: Interrupt clear transfer 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_CLEAR_TRAN(module)));
+ (*fpPrint) ("Module %d: Interrupt clear block 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_CLEAR_BLOCK(module)));
+ (*fpPrint) ("Module %d: Interrupt clear src transfer 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_CLEAR_STRAN(module)));
+ (*fpPrint) ("Module %d: Interrupt clear dst transfer 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_CLEAR_DTRAN(module)));
+ (*fpPrint) ("Module %d: Interrupt clear error 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_INT_CLEAR_ERROR(module)));
+ (*fpPrint) ("--------------------------------------------------\n");
+ (*fpPrint) ("Module %d: SW source req 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_SW_HS_SRC_REQ(module)));
+ (*fpPrint) ("Module %d: SW dest req 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_SW_HS_DST_REQ(module)));
+ (*fpPrint) ("Module %d: SW source signal 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_SW_HS_SRC_SGL_REQ(module)));
+ (*fpPrint) ("Module %d: SW dest signal 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_SW_HS_DST_SGL_REQ(module)));
+ (*fpPrint) ("Module %d: SW source last 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_SW_HS_SRC_LST_REQ(module)));
+ (*fpPrint) ("Module %d: SW dest last 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_SW_HS_DST_LST_REQ(module)));
+ (*fpPrint) ("--------------------------------------------------\n");
+ (*fpPrint) ("Module %d: misc config 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_MISC_CFG(module)));
+ (*fpPrint) ("Module %d: misc channel enable 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_MISC_CH_ENABLE(module)));
+ (*fpPrint) ("Module %d: misc ID 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_MISC_ID(module)));
+ (*fpPrint) ("Module %d: misc test 0x%X\n",
+ module, (uint32_t) (dmacHw_REG_MISC_TEST(module)));
+
+ if (channel == -1) {
+ for (chan = 0; chan < 8; chan++) {
+ (*fpPrint)
+ ("--------------------------------------------------\n");
+ (*fpPrint)
+ ("Module %d: Channel %d Source 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_SAR(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Destination 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_DAR(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d LLP 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_LLP(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Control (LO) 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_CTL_LO(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Control (HI) 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_CTL_HI(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Source Stats 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_SSTAT(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Dest Stats 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_DSTAT(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Source Stats Addr 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_SSTATAR(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Dest Stats Addr 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_DSTATAR(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Config (LO) 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_CFG_LO(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Config (HI) 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_CFG_HI(module, chan)));
+ }
+ } else {
+ chan = channel;
+ (*fpPrint)
+ ("--------------------------------------------------\n");
+ (*fpPrint)
+ ("Module %d: Channel %d Source 0x%X\n",
+ module, chan, (uint32_t) (dmacHw_REG_SAR(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Destination 0x%X\n",
+ module, chan, (uint32_t) (dmacHw_REG_DAR(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d LLP 0x%X\n",
+ module, chan, (uint32_t) (dmacHw_REG_LLP(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Control (LO) 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_CTL_LO(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Control (HI) 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_CTL_HI(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Source Stats 0x%X\n",
+ module, chan, (uint32_t) (dmacHw_REG_SSTAT(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Dest Stats 0x%X\n",
+ module, chan, (uint32_t) (dmacHw_REG_DSTAT(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Source Stats Addr 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_SSTATAR(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Dest Stats Addr 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_DSTATAR(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Config (LO) 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_CFG_LO(module, chan)));
+ (*fpPrint)
+ ("Module %d: Channel %d Config (HI) 0x%X\n",
+ module, chan,
+ (uint32_t) (dmacHw_REG_CFG_HI(module, chan)));
+ }
+}
+
+/****************************************************************************/
+/**
+* @brief Helper function to display descriptor ring
+*
+* @return void
+*
+*
+* @note
+* None
+*/
+/****************************************************************************/
+static void DisplayDescRing(void *pDescriptor, /* [ IN ] Descriptor buffer */
+ int (*fpPrint) (const char *, ...) /* [ IN ] Callback to the print function */
+ ) {
+ dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor);
+ dmacHw_DESC_t *pStart;
+
+ if (pRing->pHead == NULL) {
+ return;
+ }
+
+ pStart = pRing->pHead;
+
+ while ((dmacHw_DESC_t *) pStart->llp != pRing->pHead) {
+ if (pStart == pRing->pHead) {
+ (*fpPrint) ("Head\n");
+ }
+ if (pStart == pRing->pTail) {
+ (*fpPrint) ("Tail\n");
+ }
+ if (pStart == pRing->pProg) {
+ (*fpPrint) ("Prog\n");
+ }
+ if (pStart == pRing->pEnd) {
+ (*fpPrint) ("End\n");
+ }
+ if (pStart == pRing->pFree) {
+ (*fpPrint) ("Free\n");
+ }
+ (*fpPrint) ("0x%X:\n", (uint32_t) pStart);
+ (*fpPrint) ("sar 0x%0X\n", pStart->sar);
+ (*fpPrint) ("dar 0x%0X\n", pStart->dar);
+ (*fpPrint) ("llp 0x%0X\n", pStart->llp);
+ (*fpPrint) ("ctl.lo 0x%0X\n", pStart->ctl.lo);
+ (*fpPrint) ("ctl.hi 0x%0X\n", pStart->ctl.hi);
+ (*fpPrint) ("sstat 0x%0X\n", pStart->sstat);
+ (*fpPrint) ("dstat 0x%0X\n", pStart->dstat);
+ (*fpPrint) ("devCtl 0x%0X\n", pStart->devCtl);
+
+ pStart = (dmacHw_DESC_t *) pStart->llp;
+ }
+ if (pStart == pRing->pHead) {
+ (*fpPrint) ("Head\n");
+ }
+ if (pStart == pRing->pTail) {
+ (*fpPrint) ("Tail\n");
+ }
+ if (pStart == pRing->pProg) {
+ (*fpPrint) ("Prog\n");
+ }
+ if (pStart == pRing->pEnd) {
+ (*fpPrint) ("End\n");
+ }
+ if (pStart == pRing->pFree) {
+ (*fpPrint) ("Free\n");
+ }
+ (*fpPrint) ("0x%X:\n", (uint32_t) pStart);
+ (*fpPrint) ("sar 0x%0X\n", pStart->sar);
+ (*fpPrint) ("dar 0x%0X\n", pStart->dar);
+ (*fpPrint) ("llp 0x%0X\n", pStart->llp);
+ (*fpPrint) ("ctl.lo 0x%0X\n", pStart->ctl.lo);
+ (*fpPrint) ("ctl.hi 0x%0X\n", pStart->ctl.hi);
+ (*fpPrint) ("sstat 0x%0X\n", pStart->sstat);
+ (*fpPrint) ("dstat 0x%0X\n", pStart->dstat);
+ (*fpPrint) ("devCtl 0x%0X\n", pStart->devCtl);
+}
+
+/****************************************************************************/
+/**
+* @brief Check if DMA channel is the flow controller
+*
+* @return 1 : If DMA is a flow controler
+* 0 : Peripheral is the flow controller
+*
+* @note
+* None
+*/
+/****************************************************************************/
+static inline int DmaIsFlowController(void *pDescriptor /* [ IN ] Descriptor buffer */
+ ) {
+ uint32_t ttfc =
+ (dmacHw_GET_DESC_RING(pDescriptor))->pTail->ctl.
+ lo & dmacHw_REG_CTL_TTFC_MASK;
+
+ switch (ttfc) {
+ case dmacHw_REG_CTL_TTFC_MM_DMAC:
+ case dmacHw_REG_CTL_TTFC_MP_DMAC:
+ case dmacHw_REG_CTL_TTFC_PM_DMAC:
+ case dmacHw_REG_CTL_TTFC_PP_DMAC:
+ return 1;
+ }
+
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Overwrites data length in the descriptor
+*
+* This function overwrites data length in the descriptor
+*
+*
+* @return void
+*
+* @note
+* This is only used for PCM channel
+*/
+/****************************************************************************/
+void dmacHw_setDataLength(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ size_t dataLen /* [ IN ] Data length in bytes */
+ ) {
+ dmacHw_DESC_t *pProg;
+ dmacHw_DESC_t *pHead;
+ int srcTs = 0;
+ int srcTrSize = 0;
+
+ pHead = (dmacHw_GET_DESC_RING(pDescriptor))->pHead;
+ pProg = pHead;
+
+ srcTrSize = dmacHw_GetTrWidthInBytes(pConfig->srcMaxTransactionWidth);
+ srcTs = dataLen / srcTrSize;
+ do {
+ pProg->ctl.hi = srcTs & dmacHw_REG_CTL_BLOCK_TS_MASK;
+ pProg = (dmacHw_DESC_t *) pProg->llp;
+ } while (pProg != pHead);
+}
+
+/****************************************************************************/
+/**
+* @brief Clears the interrupt
+*
+* This function clears the DMA channel specific interrupt
+*
+*
+* @return void
+*
+* @note
+* Must be called under the context of ISR
+*/
+/****************************************************************************/
+void dmacHw_clearInterrupt(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ ) {
+ dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+
+ dmacHw_TRAN_INT_CLEAR(pCblk->module, pCblk->channel);
+ dmacHw_BLOCK_INT_CLEAR(pCblk->module, pCblk->channel);
+ dmacHw_ERROR_INT_CLEAR(pCblk->module, pCblk->channel);
+}
+
+/****************************************************************************/
+/**
+* @brief Returns the cause of channel specific DMA interrupt
+*
+* This function returns the cause of interrupt
+*
+* @return Interrupt status, each bit representing a specific type of interrupt
+*
+* @note
+* Should be called under the context of ISR
+*/
+/****************************************************************************/
+dmacHw_INTERRUPT_STATUS_e dmacHw_getInterruptStatus(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ ) {
+ dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+ dmacHw_INTERRUPT_STATUS_e status = dmacHw_INTERRUPT_STATUS_NONE;
+
+ if (dmacHw_REG_INT_STAT_TRAN(pCblk->module) &
+ ((0x00000001 << pCblk->channel))) {
+ status |= dmacHw_INTERRUPT_STATUS_TRANS;
+ }
+ if (dmacHw_REG_INT_STAT_BLOCK(pCblk->module) &
+ ((0x00000001 << pCblk->channel))) {
+ status |= dmacHw_INTERRUPT_STATUS_BLOCK;
+ }
+ if (dmacHw_REG_INT_STAT_ERROR(pCblk->module) &
+ ((0x00000001 << pCblk->channel))) {
+ status |= dmacHw_INTERRUPT_STATUS_ERROR;
+ }
+
+ return status;
+}
+
+/****************************************************************************/
+/**
+* @brief Indentifies a DMA channel causing interrupt
+*
+* This functions returns a channel causing interrupt of type dmacHw_INTERRUPT_STATUS_e
+*
+* @return NULL : No channel causing DMA interrupt
+* ! NULL : Handle to a channel causing DMA interrupt
+* @note
+* dmacHw_clearInterrupt() must be called with a valid handle after calling this function
+*/
+/****************************************************************************/
+dmacHw_HANDLE_t dmacHw_getInterruptSource(void)
+{
+ uint32_t i;
+
+ for (i = 0; i < dmaChannelCount_0 + dmaChannelCount_1; i++) {
+ if ((dmacHw_REG_INT_STAT_TRAN(dmacHw_gCblk[i].module) &
+ ((0x00000001 << dmacHw_gCblk[i].channel)))
+ || (dmacHw_REG_INT_STAT_BLOCK(dmacHw_gCblk[i].module) &
+ ((0x00000001 << dmacHw_gCblk[i].channel)))
+ || (dmacHw_REG_INT_STAT_ERROR(dmacHw_gCblk[i].module) &
+ ((0x00000001 << dmacHw_gCblk[i].channel)))
+ ) {
+ return dmacHw_CBLK_TO_HANDLE(&dmacHw_gCblk[i]);
+ }
+ }
+ return dmacHw_CBLK_TO_HANDLE(NULL);
+}
+
+/****************************************************************************/
+/**
+* @brief Estimates number of descriptor needed to perform certain DMA transfer
+*
+*
+* @return On failure : -1
+* On success : Number of descriptor count
+*
+*
+*/
+/****************************************************************************/
+int dmacHw_calculateDescriptorCount(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pSrcAddr, /* [ IN ] Source (Peripheral/Memory) address */
+ void *pDstAddr, /* [ IN ] Destination (Peripheral/Memory) address */
+ size_t dataLen /* [ IN ] Data length in bytes */
+ ) {
+ int srcTs = 0;
+ int oddSize = 0;
+ int descCount = 0;
+ int dstTrSize = 0;
+ int srcTrSize = 0;
+ uint32_t maxBlockSize = dmacHw_MAX_BLOCKSIZE;
+ dmacHw_TRANSACTION_WIDTH_e dstTrWidth;
+ dmacHw_TRANSACTION_WIDTH_e srcTrWidth;
+
+ dstTrSize = dmacHw_GetTrWidthInBytes(pConfig->dstMaxTransactionWidth);
+ srcTrSize = dmacHw_GetTrWidthInBytes(pConfig->srcMaxTransactionWidth);
+
+ /* Skip Tx if buffer is NULL or length is unknown */
+ if ((pSrcAddr == NULL) || (pDstAddr == NULL) || (dataLen == 0)) {
+ /* Do not initiate transfer */
+ return -1;
+ }
+
+ /* Ensure scatter and gather are transaction aligned */
+ if (pConfig->srcGatherWidth % srcTrSize
+ || pConfig->dstScatterWidth % dstTrSize) {
+ return -1;
+ }
+
+ /*
+ Background 1: DMAC can not perform DMA if source and destination addresses are
+ not properly aligned with the channel's transaction width. So, for successful
+ DMA transfer, transaction width must be set according to the alignment of the
+ source and destination address.
+ */
+
+ /* Adjust destination transaction width if destination address is not aligned properly */
+ dstTrWidth = pConfig->dstMaxTransactionWidth;
+ while (dmacHw_ADDRESS_MASK(dstTrSize) & (uint32_t) pDstAddr) {
+ dstTrWidth = dmacHw_GetNextTrWidth(dstTrWidth);
+ dstTrSize = dmacHw_GetTrWidthInBytes(dstTrWidth);
+ }
+
+ /* Adjust source transaction width if source address is not aligned properly */
+ srcTrWidth = pConfig->srcMaxTransactionWidth;
+ while (dmacHw_ADDRESS_MASK(srcTrSize) & (uint32_t) pSrcAddr) {
+ srcTrWidth = dmacHw_GetNextTrWidth(srcTrWidth);
+ srcTrSize = dmacHw_GetTrWidthInBytes(srcTrWidth);
+ }
+
+ /* Find the maximum transaction per descriptor */
+ if (pConfig->maxDataPerBlock
+ && ((pConfig->maxDataPerBlock / srcTrSize) <
+ dmacHw_MAX_BLOCKSIZE)) {
+ maxBlockSize = pConfig->maxDataPerBlock / srcTrSize;
+ }
+
+ /* Find number of source transactions needed to complete the DMA transfer */
+ srcTs = dataLen / srcTrSize;
+ /* Find the odd number of bytes that need to be transferred as single byte transaction width */
+ if (srcTs && (dstTrSize > srcTrSize)) {
+ oddSize = dataLen % dstTrSize;
+ /* Adjust source transaction count due to "oddSize" */
+ srcTs = srcTs - (oddSize / srcTrSize);
+ } else {
+ oddSize = dataLen % srcTrSize;
+ }
+ /* Adjust "descCount" due to "oddSize" */
+ if (oddSize) {
+ descCount++;
+ }
+
+ /* Find the number of descriptor needed for total "srcTs" */
+ if (srcTs) {
+ descCount += ((srcTs - 1) / maxBlockSize) + 1;
+ }
+
+ return descCount;
+}
+
+/****************************************************************************/
+/**
+* @brief Check the existance of pending descriptor
+*
+* This function confirmes if there is any pending descriptor in the chain
+* to program the channel
+*
+* @return 1 : Channel need to be programmed with pending descriptor
+* 0 : No more pending descriptor to programe the channel
+*
+* @note
+* - This function should be called from ISR in case there are pending
+* descriptor to program the channel.
+*
+* Example:
+*
+* dmac_isr ()
+* {
+* ...
+* if (dmacHw_descriptorPending (handle))
+* {
+* dmacHw_initiateTransfer (handle);
+* }
+* }
+*
+*/
+/****************************************************************************/
+uint32_t dmacHw_descriptorPending(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ void *pDescriptor /* [ IN ] Descriptor buffer */
+ ) {
+ dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+ dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor);
+
+ /* Make sure channel is not busy */
+ if (!CHANNEL_BUSY(pCblk->module, pCblk->channel)) {
+ /* Check if pEnd is not processed */
+ if (pRing->pEnd) {
+ /* Something left for processing */
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Program channel register to stop transfer
+*
+* Ensures the channel is not doing any transfer after calling this function
+*
+* @return void
+*
+*/
+/****************************************************************************/
+void dmacHw_stopTransfer(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ ) {
+ dmacHw_CBLK_t *pCblk;
+
+ pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+
+ /* Stop the channel */
+ dmacHw_DMA_STOP(pCblk->module, pCblk->channel);
+}
+
+/****************************************************************************/
+/**
+* @brief Deallocates source or destination memory, allocated
+*
+* This function can be called to deallocate data memory that was DMAed successfully
+*
+* @return On failure : -1
+* On success : Number of buffer freed
+*
+* @note
+* This function will be called ONLY, when source OR destination address is pointing
+* to dynamic memory
+*/
+/****************************************************************************/
+int dmacHw_freeMem(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ void (*fpFree) (void *) /* [ IN ] Function pointer to free data memory */
+ ) {
+ dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor);
+ uint32_t count = 0;
+
+ if (fpFree == NULL) {
+ return -1;
+ }
+
+ while ((pRing->pFree != pRing->pTail)
+ && (pRing->pFree->ctl.lo & dmacHw_DESC_FREE)) {
+ if (pRing->pFree->devCtl == dmacHw_FREE_USER_MEMORY) {
+ /* Identify, which memory to free */
+ if (dmacHw_DST_IS_MEMORY(pConfig->transferType)) {
+ (*fpFree) ((void *)pRing->pFree->dar);
+ } else {
+ /* Destination was a peripheral */
+ (*fpFree) ((void *)pRing->pFree->sar);
+ }
+ /* Unmark user memory to indicate it is freed */
+ pRing->pFree->devCtl = ~dmacHw_FREE_USER_MEMORY;
+ }
+ dmacHw_NEXT_DESC(pRing, pFree);
+
+ count++;
+ }
+
+ return count;
+}
+
+/****************************************************************************/
+/**
+* @brief Prepares descriptor ring, when source peripheral working as a flow controller
+*
+* This function will update the discriptor ring by allocating buffers, when source peripheral
+* has to work as a flow controller to transfer data from:
+* - Peripheral to memory.
+*
+* @return On failure : -1
+* On success : Number of descriptor updated
+*
+*
+* @note
+* Channel must be configured for peripheral to memory transfer
+*
+*/
+/****************************************************************************/
+int dmacHw_setVariableDataDescriptor(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ uint32_t srcAddr, /* [ IN ] Source peripheral address */
+ void *(*fpAlloc) (int len), /* [ IN ] Function pointer that provides destination memory */
+ int len, /* [ IN ] Number of bytes "fpAlloc" will allocate for destination */
+ int num /* [ IN ] Number of descriptor to set */
+ ) {
+ dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+ dmacHw_DESC_t *pProg = NULL;
+ dmacHw_DESC_t *pLast = NULL;
+ dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor);
+ uint32_t dstAddr;
+ uint32_t controlParam;
+ int i;
+
+ dmacHw_ASSERT(pConfig->transferType ==
+ dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM);
+
+ if (num > pRing->num) {
+ return -1;
+ }
+
+ pLast = pRing->pEnd; /* Last descriptor updated */
+ pProg = pRing->pHead; /* First descriptor in the new list */
+
+ controlParam = pConfig->srcUpdate |
+ pConfig->dstUpdate |
+ pConfig->srcMaxTransactionWidth |
+ pConfig->dstMaxTransactionWidth |
+ pConfig->srcMasterInterface |
+ pConfig->dstMasterInterface |
+ pConfig->srcMaxBurstWidth |
+ pConfig->dstMaxBurstWidth |
+ dmacHw_REG_CTL_TTFC_PM_PERI |
+ dmacHw_REG_CTL_LLP_DST_EN |
+ dmacHw_REG_CTL_LLP_SRC_EN | dmacHw_REG_CTL_INT_EN;
+
+ for (i = 0; i < num; i++) {
+ /* Allocate Rx buffer only for idle descriptor */
+ if (((pRing->pHead->ctl.hi & dmacHw_DESC_FREE) == 0) ||
+ ((dmacHw_DESC_t *) pRing->pHead->llp == pRing->pTail)
+ ) {
+ /* Rx descriptor is not idle */
+ break;
+ }
+ /* Set source address */
+ pRing->pHead->sar = srcAddr;
+ if (fpAlloc) {
+ /* Allocate memory for buffer in descriptor */
+ dstAddr = (uint32_t) (*fpAlloc) (len);
+ /* Check the destination address */
+ if (dstAddr == 0) {
+ if (i == 0) {
+ /* Not a single descriptor is available */
+ return -1;
+ }
+ break;
+ }
+ /* Set destination address */
+ pRing->pHead->dar = dstAddr;
+ }
+ /* Set control information */
+ pRing->pHead->ctl.lo = controlParam;
+ /* Use "devCtl" to mark the memory that need to be freed later */
+ pRing->pHead->devCtl = dmacHw_FREE_USER_MEMORY;
+ /* Descriptor is now owned by the channel */
+ pRing->pHead->ctl.hi = 0;
+ /* Remember the descriptor last updated */
+ pRing->pEnd = pRing->pHead;
+ /* Update next descriptor */
+ dmacHw_NEXT_DESC(pRing, pHead);
+ }
+
+ /* Mark the end of the list */
+ pRing->pEnd->ctl.lo &=
+ ~(dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN);
+ /* Connect the list */
+ if (pLast != pProg) {
+ pLast->ctl.lo |=
+ dmacHw_REG_CTL_LLP_DST_EN | dmacHw_REG_CTL_LLP_SRC_EN;
+ }
+ /* Mark the descriptors are updated */
+ pCblk->descUpdated = 1;
+ if (!pCblk->varDataStarted) {
+ /* LLP must be pointing to the first descriptor */
+ dmacHw_SET_LLP(pCblk->module, pCblk->channel,
+ (uint32_t) pProg - pRing->virt2PhyOffset);
+ /* Channel, handling variable data started */
+ pCblk->varDataStarted = 1;
+ }
+
+ return i;
+}
+
+/****************************************************************************/
+/**
+* @brief Read data DMAed to memory
+*
+* This function will read data that has been DMAed to memory while transfering from:
+* - Memory to memory
+* - Peripheral to memory
+*
+* @param handle -
+* @param ppBbuf -
+* @param pLen -
+*
+* @return 0 - No more data is available to read
+* 1 - More data might be available to read
+*
+*/
+/****************************************************************************/
+int dmacHw_readTransferredData(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ void **ppBbuf, /* [ OUT ] Data received */
+ size_t *pLlen /* [ OUT ] Length of the data received */
+ ) {
+ dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor);
+
+ (void)handle;
+
+ if (pConfig->transferMode != dmacHw_TRANSFER_MODE_CONTINUOUS) {
+ if (((pRing->pTail->ctl.hi & dmacHw_DESC_FREE) == 0) ||
+ (pRing->pTail == pRing->pHead)
+ ) {
+ /* No receive data available */
+ *ppBbuf = (char *)NULL;
+ *pLlen = 0;
+
+ return 0;
+ }
+ }
+
+ /* Return read buffer and length */
+ *ppBbuf = (char *)pRing->pTail->dar;
+
+ /* Extract length of the received data */
+ if (DmaIsFlowController(pDescriptor)) {
+ uint32_t srcTrSize = 0;
+
+ switch (pRing->pTail->ctl.lo & dmacHw_REG_CTL_SRC_TR_WIDTH_MASK) {
+ case dmacHw_REG_CTL_SRC_TR_WIDTH_8:
+ srcTrSize = 1;
+ break;
+ case dmacHw_REG_CTL_SRC_TR_WIDTH_16:
+ srcTrSize = 2;
+ break;
+ case dmacHw_REG_CTL_SRC_TR_WIDTH_32:
+ srcTrSize = 4;
+ break;
+ case dmacHw_REG_CTL_SRC_TR_WIDTH_64:
+ srcTrSize = 8;
+ break;
+ default:
+ dmacHw_ASSERT(0);
+ }
+ /* Calculate length from the block size */
+ *pLlen =
+ (pRing->pTail->ctl.hi & dmacHw_REG_CTL_BLOCK_TS_MASK) *
+ srcTrSize;
+ } else {
+ /* Extract length from the source peripheral */
+ *pLlen = pRing->pTail->sstat;
+ }
+
+ /* Advance tail to next descriptor */
+ dmacHw_NEXT_DESC(pRing, pTail);
+
+ return 1;
+}
+
+/****************************************************************************/
+/**
+* @brief Set descriptor carrying control information
+*
+* This function will be used to send specific control information to the device
+* using the DMA channel
+*
+*
+* @return -1 - On failure
+* 0 - On success
+*
+* @note
+* None
+*/
+/****************************************************************************/
+int dmacHw_setControlDescriptor(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ uint32_t ctlAddress, /* [ IN ] Address of the device control register */
+ uint32_t control /* [ IN ] Device control information */
+ ) {
+ dmacHw_DESC_RING_t *pRing = dmacHw_GET_DESC_RING(pDescriptor);
+
+ if (ctlAddress == 0) {
+ return -1;
+ }
+
+ /* Check the availability of descriptors in the ring */
+ if ((pRing->pHead->ctl.hi & dmacHw_DESC_FREE) == 0) {
+ return -1;
+ }
+ /* Set control information */
+ pRing->pHead->devCtl = control;
+ /* Set source and destination address */
+ pRing->pHead->sar = (uint32_t) &pRing->pHead->devCtl;
+ pRing->pHead->dar = ctlAddress;
+ /* Set control parameters */
+ if (pConfig->flowControler == dmacHw_FLOW_CONTROL_DMA) {
+ pRing->pHead->ctl.lo = pConfig->transferType |
+ dmacHw_SRC_ADDRESS_UPDATE_MODE_INC |
+ dmacHw_DST_ADDRESS_UPDATE_MODE_INC |
+ dmacHw_SRC_TRANSACTION_WIDTH_32 |
+ pConfig->dstMaxTransactionWidth |
+ dmacHw_SRC_BURST_WIDTH_0 |
+ dmacHw_DST_BURST_WIDTH_0 |
+ pConfig->srcMasterInterface |
+ pConfig->dstMasterInterface | dmacHw_REG_CTL_INT_EN;
+ } else {
+ uint32_t transferType = 0;
+ switch (pConfig->transferType) {
+ case dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM:
+ transferType = dmacHw_REG_CTL_TTFC_PM_PERI;
+ break;
+ case dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL:
+ transferType = dmacHw_REG_CTL_TTFC_MP_PERI;
+ break;
+ default:
+ dmacHw_ASSERT(0);
+ }
+ pRing->pHead->ctl.lo = transferType |
+ dmacHw_SRC_ADDRESS_UPDATE_MODE_INC |
+ dmacHw_DST_ADDRESS_UPDATE_MODE_INC |
+ dmacHw_SRC_TRANSACTION_WIDTH_32 |
+ pConfig->dstMaxTransactionWidth |
+ dmacHw_SRC_BURST_WIDTH_0 |
+ dmacHw_DST_BURST_WIDTH_0 |
+ pConfig->srcMasterInterface |
+ pConfig->dstMasterInterface |
+ pConfig->flowControler | dmacHw_REG_CTL_INT_EN;
+ }
+
+ /* Set block transaction size to one 32 bit transaction */
+ pRing->pHead->ctl.hi = dmacHw_REG_CTL_BLOCK_TS_MASK & 1;
+
+ /* Remember the descriptor to initialize the registers */
+ if (pRing->pProg == dmacHw_DESC_INIT) {
+ pRing->pProg = pRing->pHead;
+ }
+ pRing->pEnd = pRing->pHead;
+
+ /* Advance the descriptor */
+ dmacHw_NEXT_DESC(pRing, pHead);
+
+ /* Update Tail pointer if destination is a peripheral */
+ if (!dmacHw_DST_IS_MEMORY(pConfig->transferType)) {
+ pRing->pTail = pRing->pHead;
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Sets channel specific user data
+*
+* This function associates user data to a specif DMA channel
+*
+*/
+/****************************************************************************/
+void dmacHw_setChannelUserData(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ void *userData /* [ IN ] User data */
+ ) {
+ dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+
+ pCblk->userData = userData;
+}
+
+/****************************************************************************/
+/**
+* @brief Gets channel specific user data
+*
+* This function returns user data specific to a DMA channel
+*
+* @return user data
+*/
+/****************************************************************************/
+void *dmacHw_getChannelUserData(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ ) {
+ dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+
+ return pCblk->userData;
+}
+
+/****************************************************************************/
+/**
+* @brief Resets descriptor control information
+*
+* @return void
+*/
+/****************************************************************************/
+void dmacHw_resetDescriptorControl(void *pDescriptor /* [ IN ] Descriptor buffer */
+ ) {
+ int i;
+ dmacHw_DESC_RING_t *pRing;
+ dmacHw_DESC_t *pDesc;
+
+ pRing = dmacHw_GET_DESC_RING(pDescriptor);
+ pDesc = pRing->pHead;
+
+ for (i = 0; i < pRing->num; i++) {
+ /* Mark descriptor is ready to use */
+ pDesc->ctl.hi = dmacHw_DESC_FREE;
+ /* Look into next link list item */
+ pDesc++;
+ }
+ pRing->pFree = pRing->pTail = pRing->pEnd = pRing->pHead;
+ pRing->pProg = dmacHw_DESC_INIT;
+}
+
+/****************************************************************************/
+/**
+* @brief Displays channel specific registers and other control parameters
+*
+* @return void
+*
+*
+* @note
+* None
+*/
+/****************************************************************************/
+void dmacHw_printDebugInfo(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ int (*fpPrint) (const char *, ...) /* [ IN ] Print callback function */
+ ) {
+ dmacHw_CBLK_t *pCblk = dmacHw_HANDLE_TO_CBLK(handle);
+
+ DisplayRegisterContents(pCblk->module, pCblk->channel, fpPrint);
+ DisplayDescRing(pDescriptor, fpPrint);
+}
diff --git a/arch/arm/mach-bcmring/csp/tmr/Makefile b/arch/arm/mach-bcmring/csp/tmr/Makefile
new file mode 100644
index 00000000000..244a61ab769
--- /dev/null
+++ b/arch/arm/mach-bcmring/csp/tmr/Makefile
@@ -0,0 +1 @@
+obj-y += tmrHw.o
diff --git a/arch/arm/mach-bcmring/csp/tmr/tmrHw.c b/arch/arm/mach-bcmring/csp/tmr/tmrHw.c
new file mode 100644
index 00000000000..5c1c9a0e5ed
--- /dev/null
+++ b/arch/arm/mach-bcmring/csp/tmr/tmrHw.c
@@ -0,0 +1,576 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file tmrHw.c
+*
+* @brief Low level Timer driver routines
+*
+* @note
+*
+* These routines provide basic timer functionality only.
+*/
+/****************************************************************************/
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <csp/errno.h>
+#include <csp/stdint.h>
+
+#include <csp/tmrHw.h>
+#include <mach/csp/tmrHw_reg.h>
+
+#define tmrHw_ASSERT(a) if (!(a)) *(char *)0 = 0
+#define tmrHw_MILLISEC_PER_SEC (1000)
+
+#define tmrHw_LOW_1_RESOLUTION_COUNT (tmrHw_LOW_RESOLUTION_CLOCK / tmrHw_MILLISEC_PER_SEC)
+#define tmrHw_LOW_1_MAX_MILLISEC (0xFFFFFFFF / tmrHw_LOW_1_RESOLUTION_COUNT)
+#define tmrHw_LOW_16_RESOLUTION_COUNT (tmrHw_LOW_1_RESOLUTION_COUNT / 16)
+#define tmrHw_LOW_16_MAX_MILLISEC (0xFFFFFFFF / tmrHw_LOW_16_RESOLUTION_COUNT)
+#define tmrHw_LOW_256_RESOLUTION_COUNT (tmrHw_LOW_1_RESOLUTION_COUNT / 256)
+#define tmrHw_LOW_256_MAX_MILLISEC (0xFFFFFFFF / tmrHw_LOW_256_RESOLUTION_COUNT)
+
+#define tmrHw_HIGH_1_RESOLUTION_COUNT (tmrHw_HIGH_RESOLUTION_CLOCK / tmrHw_MILLISEC_PER_SEC)
+#define tmrHw_HIGH_1_MAX_MILLISEC (0xFFFFFFFF / tmrHw_HIGH_1_RESOLUTION_COUNT)
+#define tmrHw_HIGH_16_RESOLUTION_COUNT (tmrHw_HIGH_1_RESOLUTION_COUNT / 16)
+#define tmrHw_HIGH_16_MAX_MILLISEC (0xFFFFFFFF / tmrHw_HIGH_16_RESOLUTION_COUNT)
+#define tmrHw_HIGH_256_RESOLUTION_COUNT (tmrHw_HIGH_1_RESOLUTION_COUNT / 256)
+#define tmrHw_HIGH_256_MAX_MILLISEC (0xFFFFFFFF / tmrHw_HIGH_256_RESOLUTION_COUNT)
+
+static void ResetTimer(tmrHw_ID_t timerId)
+ __attribute__ ((section(".aramtext")));
+static int tmrHw_divide(int num, int denom)
+ __attribute__ ((section(".aramtext")));
+
+/****************************************************************************/
+/**
+* @brief Get timer capability
+*
+* This function returns various capabilities/attributes of a timer
+*
+* @return Capability
+*
+*/
+/****************************************************************************/
+uint32_t tmrHw_getTimerCapability(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
+ tmrHw_CAPABILITY_e capability /* [ IN ] Timer capability */
+) {
+ switch (capability) {
+ case tmrHw_CAPABILITY_CLOCK:
+ return (timerId <=
+ 1) ? tmrHw_LOW_RESOLUTION_CLOCK :
+ tmrHw_HIGH_RESOLUTION_CLOCK;
+ case tmrHw_CAPABILITY_RESOLUTION:
+ return 32;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Resets a timer
+*
+* This function initializes timer
+*
+* @return void
+*
+*/
+/****************************************************************************/
+static void ResetTimer(tmrHw_ID_t timerId /* [ IN ] Timer Id */
+) {
+ /* Reset timer */
+ pTmrHw[timerId].LoadValue = 0;
+ pTmrHw[timerId].CurrentValue = 0xFFFFFFFF;
+ pTmrHw[timerId].Control = 0;
+ pTmrHw[timerId].BackgroundLoad = 0;
+ /* Always configure as a 32 bit timer */
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_32BIT;
+ /* Clear interrupt only if raw status interrupt is set */
+ if (pTmrHw[timerId].RawInterruptStatus) {
+ pTmrHw[timerId].InterruptClear = 0xFFFFFFFF;
+ }
+}
+
+/****************************************************************************/
+/**
+* @brief Sets counter value for an interval in ms
+*
+* @return On success: Effective counter value set
+* On failure: 0
+*
+*/
+/****************************************************************************/
+static tmrHw_INTERVAL_t SetTimerPeriod(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
+ tmrHw_INTERVAL_t msec /* [ IN ] Interval in milli-second */
+) {
+ uint32_t scale = 0;
+ uint32_t count = 0;
+
+ if (timerId == 0 || timerId == 1) {
+ if (msec <= tmrHw_LOW_1_MAX_MILLISEC) {
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1;
+ scale = tmrHw_LOW_1_RESOLUTION_COUNT;
+ } else if (msec <= tmrHw_LOW_16_MAX_MILLISEC) {
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_16;
+ scale = tmrHw_LOW_16_RESOLUTION_COUNT;
+ } else if (msec <= tmrHw_LOW_256_MAX_MILLISEC) {
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_256;
+ scale = tmrHw_LOW_256_RESOLUTION_COUNT;
+ } else {
+ return 0;
+ }
+
+ count = msec * scale;
+ /* Set counter value */
+ pTmrHw[timerId].LoadValue = count;
+ pTmrHw[timerId].BackgroundLoad = count;
+
+ } else if (timerId == 2 || timerId == 3) {
+ if (msec <= tmrHw_HIGH_1_MAX_MILLISEC) {
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1;
+ scale = tmrHw_HIGH_1_RESOLUTION_COUNT;
+ } else if (msec <= tmrHw_HIGH_16_MAX_MILLISEC) {
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_16;
+ scale = tmrHw_HIGH_16_RESOLUTION_COUNT;
+ } else if (msec <= tmrHw_HIGH_256_MAX_MILLISEC) {
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_256;
+ scale = tmrHw_HIGH_256_RESOLUTION_COUNT;
+ } else {
+ return 0;
+ }
+
+ count = msec * scale;
+ /* Set counter value */
+ pTmrHw[timerId].LoadValue = count;
+ pTmrHw[timerId].BackgroundLoad = count;
+ }
+ return count / scale;
+}
+
+/****************************************************************************/
+/**
+* @brief Configures a periodic timer in terms of timer interrupt rate
+*
+* This function initializes a periodic timer to generate specific number of
+* timer interrupt per second
+*
+* @return On success: Effective timer frequency
+* On failure: 0
+*
+*/
+/****************************************************************************/
+tmrHw_RATE_t tmrHw_setPeriodicTimerRate(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
+ tmrHw_RATE_t rate /* [ IN ] Number of timer interrupt per second */
+) {
+ uint32_t resolution = 0;
+ uint32_t count = 0;
+ ResetTimer(timerId);
+
+ /* Set timer mode periodic */
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PERIODIC;
+ pTmrHw[timerId].Control &= ~tmrHw_CONTROL_ONESHOT;
+ /* Set timer in highest resolution */
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1;
+
+ if (rate && (timerId == 0 || timerId == 1)) {
+ if (rate > tmrHw_LOW_RESOLUTION_CLOCK) {
+ return 0;
+ }
+ resolution = tmrHw_LOW_RESOLUTION_CLOCK;
+ } else if (rate && (timerId == 2 || timerId == 3)) {
+ if (rate > tmrHw_HIGH_RESOLUTION_CLOCK) {
+ return 0;
+ } else {
+ resolution = tmrHw_HIGH_RESOLUTION_CLOCK;
+ }
+ } else {
+ return 0;
+ }
+ /* Find the counter value */
+ count = resolution / rate;
+ /* Set counter value */
+ pTmrHw[timerId].LoadValue = count;
+ pTmrHw[timerId].BackgroundLoad = count;
+
+ return resolution / count;
+}
+
+/****************************************************************************/
+/**
+* @brief Configures a periodic timer to generate timer interrupt after
+* certain time interval
+*
+* This function initializes a periodic timer to generate timer interrupt
+* after every time interval in millisecond
+*
+* @return On success: Effective interval set in milli-second
+* On failure: 0
+*
+*/
+/****************************************************************************/
+tmrHw_INTERVAL_t tmrHw_setPeriodicTimerInterval(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
+ tmrHw_INTERVAL_t msec /* [ IN ] Interval in milli-second */
+) {
+ ResetTimer(timerId);
+
+ /* Set timer mode periodic */
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PERIODIC;
+ pTmrHw[timerId].Control &= ~tmrHw_CONTROL_ONESHOT;
+
+ return SetTimerPeriod(timerId, msec);
+}
+
+/****************************************************************************/
+/**
+* @brief Configures a periodic timer to generate timer interrupt just once
+* after certain time interval
+*
+* This function initializes a periodic timer to generate a single ticks after
+* certain time interval in millisecond
+*
+* @return On success: Effective interval set in milli-second
+* On failure: 0
+*
+*/
+/****************************************************************************/
+tmrHw_INTERVAL_t tmrHw_setOneshotTimerInterval(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
+ tmrHw_INTERVAL_t msec /* [ IN ] Interval in milli-second */
+) {
+ ResetTimer(timerId);
+
+ /* Set timer mode oneshot */
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PERIODIC;
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_ONESHOT;
+
+ return SetTimerPeriod(timerId, msec);
+}
+
+/****************************************************************************/
+/**
+* @brief Configures a timer to run as a free running timer
+*
+* This function initializes a timer to run as a free running timer
+*
+* @return Timer resolution (count / sec)
+*
+*/
+/****************************************************************************/
+tmrHw_RATE_t tmrHw_setFreeRunningTimer(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
+ uint32_t divider /* [ IN ] Dividing the clock frequency */
+) {
+ uint32_t scale = 0;
+
+ ResetTimer(timerId);
+ /* Set timer as free running mode */
+ pTmrHw[timerId].Control &= ~tmrHw_CONTROL_PERIODIC;
+ pTmrHw[timerId].Control &= ~tmrHw_CONTROL_ONESHOT;
+
+ if (divider >= 64) {
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_256;
+ scale = 256;
+ } else if (divider >= 8) {
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_16;
+ scale = 16;
+ } else {
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_PRESCALE_1;
+ scale = 1;
+ }
+
+ if (timerId == 0 || timerId == 1) {
+ return tmrHw_divide(tmrHw_LOW_RESOLUTION_CLOCK, scale);
+ } else if (timerId == 2 || timerId == 3) {
+ return tmrHw_divide(tmrHw_HIGH_RESOLUTION_CLOCK, scale);
+ }
+
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Starts a timer
+*
+* This function starts a preconfigured timer
+*
+* @return -1 - On Failure
+* 0 - On Success
+*
+*/
+/****************************************************************************/
+int tmrHw_startTimer(tmrHw_ID_t timerId /* [ IN ] Timer id */
+) {
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_TIMER_ENABLE;
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Stops a timer
+*
+* This function stops a running timer
+*
+* @return -1 - On Failure
+* 0 - On Success
+*
+*/
+/****************************************************************************/
+int tmrHw_stopTimer(tmrHw_ID_t timerId /* [ IN ] Timer id */
+) {
+ pTmrHw[timerId].Control &= ~tmrHw_CONTROL_TIMER_ENABLE;
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Gets current timer count
+*
+* This function returns the current timer value
+*
+* @return Current downcounting timer value
+*
+*/
+/****************************************************************************/
+uint32_t tmrHw_GetCurrentCount(tmrHw_ID_t timerId /* [ IN ] Timer id */
+) {
+ /* return 32 bit timer value */
+ switch (pTmrHw[timerId].Control & tmrHw_CONTROL_MODE_MASK) {
+ case tmrHw_CONTROL_FREE_RUNNING:
+ if (pTmrHw[timerId].CurrentValue) {
+ return tmrHw_MAX_COUNT - pTmrHw[timerId].CurrentValue;
+ }
+ break;
+ case tmrHw_CONTROL_PERIODIC:
+ case tmrHw_CONTROL_ONESHOT:
+ return pTmrHw[timerId].BackgroundLoad -
+ pTmrHw[timerId].CurrentValue;
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Gets timer count rate
+*
+* This function returns the number of counts per second
+*
+* @return Count rate
+*
+*/
+/****************************************************************************/
+tmrHw_RATE_t tmrHw_getCountRate(tmrHw_ID_t timerId /* [ IN ] Timer id */
+) {
+ uint32_t divider = 0;
+
+ switch (pTmrHw[timerId].Control & tmrHw_CONTROL_PRESCALE_MASK) {
+ case tmrHw_CONTROL_PRESCALE_1:
+ divider = 1;
+ break;
+ case tmrHw_CONTROL_PRESCALE_16:
+ divider = 16;
+ break;
+ case tmrHw_CONTROL_PRESCALE_256:
+ divider = 256;
+ break;
+ default:
+ tmrHw_ASSERT(0);
+ }
+
+ if (timerId == 0 || timerId == 1) {
+ return tmrHw_divide(tmrHw_LOW_RESOLUTION_CLOCK, divider);
+ } else {
+ return tmrHw_divide(tmrHw_HIGH_RESOLUTION_CLOCK, divider);
+ }
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Enables timer interrupt
+*
+* This function enables the timer interrupt
+*
+* @return N/A
+*
+*/
+/****************************************************************************/
+void tmrHw_enableInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */
+) {
+ pTmrHw[timerId].Control |= tmrHw_CONTROL_INTERRUPT_ENABLE;
+}
+
+/****************************************************************************/
+/**
+* @brief Disables timer interrupt
+*
+* This function disable the timer interrupt
+*
+* @return N/A
+*
+*/
+/****************************************************************************/
+void tmrHw_disableInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */
+) {
+ pTmrHw[timerId].Control &= ~tmrHw_CONTROL_INTERRUPT_ENABLE;
+}
+
+/****************************************************************************/
+/**
+* @brief Clears the interrupt
+*
+* This function clears the timer interrupt
+*
+* @return N/A
+*
+* @note
+* Must be called under the context of ISR
+*/
+/****************************************************************************/
+void tmrHw_clearInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */
+) {
+ pTmrHw[timerId].InterruptClear = 0x1;
+}
+
+/****************************************************************************/
+/**
+* @brief Gets the interrupt status
+*
+* This function returns timer interrupt status
+*
+* @return Interrupt status
+*/
+/****************************************************************************/
+tmrHw_INTERRUPT_STATUS_e tmrHw_getInterruptStatus(tmrHw_ID_t timerId /* [ IN ] Timer id */
+) {
+ if (pTmrHw[timerId].InterruptStatus) {
+ return tmrHw_INTERRUPT_STATUS_SET;
+ } else {
+ return tmrHw_INTERRUPT_STATUS_UNSET;
+ }
+}
+
+/****************************************************************************/
+/**
+* @brief Indentifies a timer causing interrupt
+*
+* This functions returns a timer causing interrupt
+*
+* @return 0xFFFFFFFF : No timer causing an interrupt
+* ! 0xFFFFFFFF : timer causing an interrupt
+* @note
+* tmrHw_clearIntrrupt() must be called with a valid timer id after calling this function
+*/
+/****************************************************************************/
+tmrHw_ID_t tmrHw_getInterruptSource(void /* void */
+) {
+ int i;
+
+ for (i = 0; i < tmrHw_TIMER_NUM_COUNT; i++) {
+ if (pTmrHw[i].InterruptStatus) {
+ return i;
+ }
+ }
+
+ return 0xFFFFFFFF;
+}
+
+/****************************************************************************/
+/**
+* @brief Displays specific timer registers
+*
+*
+* @return void
+*
+*/
+/****************************************************************************/
+void tmrHw_printDebugInfo(tmrHw_ID_t timerId, /* [ IN ] Timer id */
+ int (*fpPrint) (const char *, ...) /* [ IN ] Print callback function */
+) {
+ (*fpPrint) ("Displaying register contents \n\n");
+ (*fpPrint) ("Timer %d: Load value 0x%X\n", timerId,
+ pTmrHw[timerId].LoadValue);
+ (*fpPrint) ("Timer %d: Background load value 0x%X\n", timerId,
+ pTmrHw[timerId].BackgroundLoad);
+ (*fpPrint) ("Timer %d: Control 0x%X\n", timerId,
+ pTmrHw[timerId].Control);
+ (*fpPrint) ("Timer %d: Interrupt clear 0x%X\n", timerId,
+ pTmrHw[timerId].InterruptClear);
+ (*fpPrint) ("Timer %d: Interrupt raw interrupt 0x%X\n", timerId,
+ pTmrHw[timerId].RawInterruptStatus);
+ (*fpPrint) ("Timer %d: Interrupt status 0x%X\n", timerId,
+ pTmrHw[timerId].InterruptStatus);
+}
+
+/****************************************************************************/
+/**
+* @brief Use a timer to perform a busy wait delay for a number of usecs.
+*
+* @return N/A
+*/
+/****************************************************************************/
+void tmrHw_udelay(tmrHw_ID_t timerId, /* [ IN ] Timer id */
+ unsigned long usecs /* [ IN ] usec to delay */
+) {
+ tmrHw_RATE_t usec_tick_rate;
+ tmrHw_COUNT_t start_time;
+ tmrHw_COUNT_t delta_time;
+
+ start_time = tmrHw_GetCurrentCount(timerId);
+ usec_tick_rate = tmrHw_divide(tmrHw_getCountRate(timerId), 1000000);
+ delta_time = usecs * usec_tick_rate;
+
+ /* Busy wait */
+ while (delta_time > (tmrHw_GetCurrentCount(timerId) - start_time))
+ ;
+}
+
+/****************************************************************************/
+/**
+* @brief Local Divide function
+*
+* This function does the divide
+*
+* @return divide value
+*
+*/
+/****************************************************************************/
+static int tmrHw_divide(int num, int denom)
+{
+ int r;
+ int t = 1;
+
+ /* Shift denom and t up to the largest value to optimize algorithm */
+ /* t contains the units of each divide */
+ while ((denom & 0x40000000) == 0) { /* fails if denom=0 */
+ denom = denom << 1;
+ t = t << 1;
+ }
+
+ /* Intialize the result */
+ r = 0;
+
+ do {
+ /* Determine if there exists a positive remainder */
+ if ((num - denom) >= 0) {
+ /* Accumlate t to the result and calculate a new remainder */
+ num = num - denom;
+ r = r + t;
+ }
+ /* Continue to shift denom and shift t down to 0 */
+ denom = denom >> 1;
+ t = t >> 1;
+ } while (t != 0);
+ return r;
+}
diff --git a/arch/arm/mach-bcmring/dma.c b/arch/arm/mach-bcmring/dma.c
new file mode 100644
index 00000000000..7b20fccb9d4
--- /dev/null
+++ b/arch/arm/mach-bcmring/dma.c
@@ -0,0 +1,2321 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file dma.c
+*
+* @brief Implements the DMA interface.
+*/
+/****************************************************************************/
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/proc_fs.h>
+
+#include <mach/timer.h>
+
+#include <linux/mm.h>
+#include <linux/pfn.h>
+#include <asm/atomic.h>
+#include <mach/dma.h>
+
+/* I don't quite understand why dc4 fails when this is set to 1 and DMA is enabled */
+/* especially since dc4 doesn't use kmalloc'd memory. */
+
+#define ALLOW_MAP_OF_KMALLOC_MEMORY 0
+
+/* ---- Public Variables ------------------------------------------------- */
+
+/* ---- Private Constants and Types -------------------------------------- */
+
+#define MAKE_HANDLE(controllerIdx, channelIdx) (((controllerIdx) << 4) | (channelIdx))
+
+#define CONTROLLER_FROM_HANDLE(handle) (((handle) >> 4) & 0x0f)
+#define CHANNEL_FROM_HANDLE(handle) ((handle) & 0x0f)
+
+#define DMA_MAP_DEBUG 0
+
+#if DMA_MAP_DEBUG
+# define DMA_MAP_PRINT(fmt, args...) printk("%s: " fmt, __func__, ## args)
+#else
+# define DMA_MAP_PRINT(fmt, args...)
+#endif
+
+/* ---- Private Variables ------------------------------------------------ */
+
+static DMA_Global_t gDMA;
+static struct proc_dir_entry *gDmaDir;
+
+static atomic_t gDmaStatMemTypeKmalloc = ATOMIC_INIT(0);
+static atomic_t gDmaStatMemTypeVmalloc = ATOMIC_INIT(0);
+static atomic_t gDmaStatMemTypeUser = ATOMIC_INIT(0);
+static atomic_t gDmaStatMemTypeCoherent = ATOMIC_INIT(0);
+
+#include "dma_device.c"
+
+/* ---- Private Function Prototypes -------------------------------------- */
+
+/* ---- Functions ------------------------------------------------------- */
+
+/****************************************************************************/
+/**
+* Displays information for /proc/dma/mem-type
+*/
+/****************************************************************************/
+
+static int dma_proc_read_mem_type(char *buf, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += sprintf(buf + len, "dma_map_mem statistics\n");
+ len +=
+ sprintf(buf + len, "coherent: %d\n",
+ atomic_read(&gDmaStatMemTypeCoherent));
+ len +=
+ sprintf(buf + len, "kmalloc: %d\n",
+ atomic_read(&gDmaStatMemTypeKmalloc));
+ len +=
+ sprintf(buf + len, "vmalloc: %d\n",
+ atomic_read(&gDmaStatMemTypeVmalloc));
+ len +=
+ sprintf(buf + len, "user: %d\n",
+ atomic_read(&gDmaStatMemTypeUser));
+
+ return len;
+}
+
+/****************************************************************************/
+/**
+* Displays information for /proc/dma/channels
+*/
+/****************************************************************************/
+
+static int dma_proc_read_channels(char *buf, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ int controllerIdx;
+ int channelIdx;
+ int limit = count - 200;
+ int len = 0;
+ DMA_Channel_t *channel;
+
+ if (down_interruptible(&gDMA.lock) < 0) {
+ return -ERESTARTSYS;
+ }
+
+ for (controllerIdx = 0; controllerIdx < DMA_NUM_CONTROLLERS;
+ controllerIdx++) {
+ for (channelIdx = 0; channelIdx < DMA_NUM_CHANNELS;
+ channelIdx++) {
+ if (len >= limit) {
+ break;
+ }
+
+ channel =
+ &gDMA.controller[controllerIdx].channel[channelIdx];
+
+ len +=
+ sprintf(buf + len, "%d:%d ", controllerIdx,
+ channelIdx);
+
+ if ((channel->flags & DMA_CHANNEL_FLAG_IS_DEDICATED) !=
+ 0) {
+ len +=
+ sprintf(buf + len, "Dedicated for %s ",
+ DMA_gDeviceAttribute[channel->
+ devType].name);
+ } else {
+ len += sprintf(buf + len, "Shared ");
+ }
+
+ if ((channel->flags & DMA_CHANNEL_FLAG_NO_ISR) != 0) {
+ len += sprintf(buf + len, "No ISR ");
+ }
+
+ if ((channel->flags & DMA_CHANNEL_FLAG_LARGE_FIFO) != 0) {
+ len += sprintf(buf + len, "Fifo: 128 ");
+ } else {
+ len += sprintf(buf + len, "Fifo: 64 ");
+ }
+
+ if ((channel->flags & DMA_CHANNEL_FLAG_IN_USE) != 0) {
+ len +=
+ sprintf(buf + len, "InUse by %s",
+ DMA_gDeviceAttribute[channel->
+ devType].name);
+#if (DMA_DEBUG_TRACK_RESERVATION)
+ len +=
+ sprintf(buf + len, " (%s:%d)",
+ channel->fileName,
+ channel->lineNum);
+#endif
+ } else {
+ len += sprintf(buf + len, "Avail ");
+ }
+
+ if (channel->lastDevType != DMA_DEVICE_NONE) {
+ len +=
+ sprintf(buf + len, "Last use: %s ",
+ DMA_gDeviceAttribute[channel->
+ lastDevType].
+ name);
+ }
+
+ len += sprintf(buf + len, "\n");
+ }
+ }
+ up(&gDMA.lock);
+ *eof = 1;
+
+ return len;
+}
+
+/****************************************************************************/
+/**
+* Displays information for /proc/dma/devices
+*/
+/****************************************************************************/
+
+static int dma_proc_read_devices(char *buf, char **start, off_t offset,
+ int count, int *eof, void *data)
+{
+ int limit = count - 200;
+ int len = 0;
+ int devIdx;
+
+ if (down_interruptible(&gDMA.lock) < 0) {
+ return -ERESTARTSYS;
+ }
+
+ for (devIdx = 0; devIdx < DMA_NUM_DEVICE_ENTRIES; devIdx++) {
+ DMA_DeviceAttribute_t *devAttr = &DMA_gDeviceAttribute[devIdx];
+
+ if (devAttr->name == NULL) {
+ continue;
+ }
+
+ if (len >= limit) {
+ break;
+ }
+
+ len += sprintf(buf + len, "%-12s ", devAttr->name);
+
+ if ((devAttr->flags & DMA_DEVICE_FLAG_IS_DEDICATED) != 0) {
+ len +=
+ sprintf(buf + len, "Dedicated %d:%d ",
+ devAttr->dedicatedController,
+ devAttr->dedicatedChannel);
+ } else {
+ len += sprintf(buf + len, "Shared DMA:");
+ if ((devAttr->flags & DMA_DEVICE_FLAG_ON_DMA0) != 0) {
+ len += sprintf(buf + len, "0");
+ }
+ if ((devAttr->flags & DMA_DEVICE_FLAG_ON_DMA1) != 0) {
+ len += sprintf(buf + len, "1");
+ }
+ len += sprintf(buf + len, " ");
+ }
+ if ((devAttr->flags & DMA_DEVICE_FLAG_NO_ISR) != 0) {
+ len += sprintf(buf + len, "NoISR ");
+ }
+ if ((devAttr->flags & DMA_DEVICE_FLAG_ALLOW_LARGE_FIFO) != 0) {
+ len += sprintf(buf + len, "Allow-128 ");
+ }
+
+ len +=
+ sprintf(buf + len,
+ "Xfer #: %Lu Ticks: %Lu Bytes: %Lu DescLen: %u\n",
+ devAttr->numTransfers, devAttr->transferTicks,
+ devAttr->transferBytes,
+ devAttr->ring.bytesAllocated);
+
+ }
+
+ up(&gDMA.lock);
+ *eof = 1;
+
+ return len;
+}
+
+/****************************************************************************/
+/**
+* Determines if a DMA_Device_t is "valid".
+*
+* @return
+* TRUE - dma device is valid
+* FALSE - dma device isn't valid
+*/
+/****************************************************************************/
+
+static inline int IsDeviceValid(DMA_Device_t device)
+{
+ return (device >= 0) && (device < DMA_NUM_DEVICE_ENTRIES);
+}
+
+/****************************************************************************/
+/**
+* Translates a DMA handle into a pointer to a channel.
+*
+* @return
+* non-NULL - pointer to DMA_Channel_t
+* NULL - DMA Handle was invalid
+*/
+/****************************************************************************/
+
+static inline DMA_Channel_t *HandleToChannel(DMA_Handle_t handle)
+{
+ int controllerIdx;
+ int channelIdx;
+
+ controllerIdx = CONTROLLER_FROM_HANDLE(handle);
+ channelIdx = CHANNEL_FROM_HANDLE(handle);
+
+ if ((controllerIdx > DMA_NUM_CONTROLLERS)
+ || (channelIdx > DMA_NUM_CHANNELS)) {
+ return NULL;
+ }
+ return &gDMA.controller[controllerIdx].channel[channelIdx];
+}
+
+/****************************************************************************/
+/**
+* Interrupt handler which is called to process DMA interrupts.
+*/
+/****************************************************************************/
+
+static irqreturn_t dma_interrupt_handler(int irq, void *dev_id)
+{
+ DMA_Channel_t *channel;
+ DMA_DeviceAttribute_t *devAttr;
+ int irqStatus;
+
+ channel = (DMA_Channel_t *) dev_id;
+
+ /* Figure out why we were called, and knock down the interrupt */
+
+ irqStatus = dmacHw_getInterruptStatus(channel->dmacHwHandle);
+ dmacHw_clearInterrupt(channel->dmacHwHandle);
+
+ if ((channel->devType < 0)
+ || (channel->devType > DMA_NUM_DEVICE_ENTRIES)) {
+ printk(KERN_ERR "dma_interrupt_handler: Invalid devType: %d\n",
+ channel->devType);
+ return IRQ_NONE;
+ }
+ devAttr = &DMA_gDeviceAttribute[channel->devType];
+
+ /* Update stats */
+
+ if ((irqStatus & dmacHw_INTERRUPT_STATUS_TRANS) != 0) {
+ devAttr->transferTicks +=
+ (timer_get_tick_count() - devAttr->transferStartTime);
+ }
+
+ if ((irqStatus & dmacHw_INTERRUPT_STATUS_ERROR) != 0) {
+ printk(KERN_ERR
+ "dma_interrupt_handler: devType :%d DMA error (%s)\n",
+ channel->devType, devAttr->name);
+ } else {
+ devAttr->numTransfers++;
+ devAttr->transferBytes += devAttr->numBytes;
+ }
+
+ /* Call any installed handler */
+
+ if (devAttr->devHandler != NULL) {
+ devAttr->devHandler(channel->devType, irqStatus,
+ devAttr->userData);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/****************************************************************************/
+/**
+* Allocates memory to hold a descriptor ring. The descriptor ring then
+* needs to be populated by making one or more calls to
+* dna_add_descriptors.
+*
+* The returned descriptor ring will be automatically initialized.
+*
+* @return
+* 0 Descriptor ring was allocated successfully
+* -EINVAL Invalid parameters passed in
+* -ENOMEM Unable to allocate memory for the desired number of descriptors.
+*/
+/****************************************************************************/
+
+int dma_alloc_descriptor_ring(DMA_DescriptorRing_t *ring, /* Descriptor ring to populate */
+ int numDescriptors /* Number of descriptors that need to be allocated. */
+ ) {
+ size_t bytesToAlloc = dmacHw_descriptorLen(numDescriptors);
+
+ if ((ring == NULL) || (numDescriptors <= 0)) {
+ return -EINVAL;
+ }
+
+ ring->physAddr = 0;
+ ring->descriptorsAllocated = 0;
+ ring->bytesAllocated = 0;
+
+ ring->virtAddr = dma_alloc_writecombine(NULL,
+ bytesToAlloc,
+ &ring->physAddr,
+ GFP_KERNEL);
+ if (ring->virtAddr == NULL) {
+ return -ENOMEM;
+ }
+
+ ring->bytesAllocated = bytesToAlloc;
+ ring->descriptorsAllocated = numDescriptors;
+
+ return dma_init_descriptor_ring(ring, numDescriptors);
+}
+
+EXPORT_SYMBOL(dma_alloc_descriptor_ring);
+
+/****************************************************************************/
+/**
+* Releases the memory which was previously allocated for a descriptor ring.
+*/
+/****************************************************************************/
+
+void dma_free_descriptor_ring(DMA_DescriptorRing_t *ring /* Descriptor to release */
+ ) {
+ if (ring->virtAddr != NULL) {
+ dma_free_writecombine(NULL,
+ ring->bytesAllocated,
+ ring->virtAddr, ring->physAddr);
+ }
+
+ ring->bytesAllocated = 0;
+ ring->descriptorsAllocated = 0;
+ ring->virtAddr = NULL;
+ ring->physAddr = 0;
+}
+
+EXPORT_SYMBOL(dma_free_descriptor_ring);
+
+/****************************************************************************/
+/**
+* Initializes a descriptor ring, so that descriptors can be added to it.
+* Once a descriptor ring has been allocated, it may be reinitialized for
+* use with additional/different regions of memory.
+*
+* Note that if 7 descriptors are allocated, it's perfectly acceptable to
+* initialize the ring with a smaller number of descriptors. The amount
+* of memory allocated for the descriptor ring will not be reduced, and
+* the descriptor ring may be reinitialized later
+*
+* @return
+* 0 Descriptor ring was initialized successfully
+* -ENOMEM The descriptor which was passed in has insufficient space
+* to hold the desired number of descriptors.
+*/
+/****************************************************************************/
+
+int dma_init_descriptor_ring(DMA_DescriptorRing_t *ring, /* Descriptor ring to initialize */
+ int numDescriptors /* Number of descriptors to initialize. */
+ ) {
+ if (ring->virtAddr == NULL) {
+ return -EINVAL;
+ }
+ if (dmacHw_initDescriptor(ring->virtAddr,
+ ring->physAddr,
+ ring->bytesAllocated, numDescriptors) < 0) {
+ printk(KERN_ERR
+ "dma_init_descriptor_ring: dmacHw_initDescriptor failed\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_init_descriptor_ring);
+
+/****************************************************************************/
+/**
+* Determines the number of descriptors which would be required for a
+* transfer of the indicated memory region.
+*
+* This function also needs to know which DMA device this transfer will
+* be destined for, so that the appropriate DMA configuration can be retrieved.
+* DMA parameters such as transfer width, and whether this is a memory-to-memory
+* or memory-to-peripheral, etc can all affect the actual number of descriptors
+* required.
+*
+* @return
+* > 0 Returns the number of descriptors required for the indicated transfer
+* -ENODEV - Device handed in is invalid.
+* -EINVAL Invalid parameters
+* -ENOMEM Memory exhausted
+*/
+/****************************************************************************/
+
+int dma_calculate_descriptor_count(DMA_Device_t device, /* DMA Device that this will be associated with */
+ dma_addr_t srcData, /* Place to get data to write to device */
+ dma_addr_t dstData, /* Pointer to device data address */
+ size_t numBytes /* Number of bytes to transfer to the device */
+ ) {
+ int numDescriptors;
+ DMA_DeviceAttribute_t *devAttr;
+
+ if (!IsDeviceValid(device)) {
+ return -ENODEV;
+ }
+ devAttr = &DMA_gDeviceAttribute[device];
+
+ numDescriptors = dmacHw_calculateDescriptorCount(&devAttr->config,
+ (void *)srcData,
+ (void *)dstData,
+ numBytes);
+ if (numDescriptors < 0) {
+ printk(KERN_ERR
+ "dma_calculate_descriptor_count: dmacHw_calculateDescriptorCount failed\n");
+ return -EINVAL;
+ }
+
+ return numDescriptors;
+}
+
+EXPORT_SYMBOL(dma_calculate_descriptor_count);
+
+/****************************************************************************/
+/**
+* Adds a region of memory to the descriptor ring. Note that it may take
+* multiple descriptors for each region of memory. It is the callers
+* responsibility to allocate a sufficiently large descriptor ring.
+*
+* @return
+* 0 Descriptors were added successfully
+* -ENODEV Device handed in is invalid.
+* -EINVAL Invalid parameters
+* -ENOMEM Memory exhausted
+*/
+/****************************************************************************/
+
+int dma_add_descriptors(DMA_DescriptorRing_t *ring, /* Descriptor ring to add descriptors to */
+ DMA_Device_t device, /* DMA Device that descriptors are for */
+ dma_addr_t srcData, /* Place to get data (memory or device) */
+ dma_addr_t dstData, /* Place to put data (memory or device) */
+ size_t numBytes /* Number of bytes to transfer to the device */
+ ) {
+ int rc;
+ DMA_DeviceAttribute_t *devAttr;
+
+ if (!IsDeviceValid(device)) {
+ return -ENODEV;
+ }
+ devAttr = &DMA_gDeviceAttribute[device];
+
+ rc = dmacHw_setDataDescriptor(&devAttr->config,
+ ring->virtAddr,
+ (void *)srcData,
+ (void *)dstData, numBytes);
+ if (rc < 0) {
+ printk(KERN_ERR
+ "dma_add_descriptors: dmacHw_setDataDescriptor failed with code: %d\n",
+ rc);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_add_descriptors);
+
+/****************************************************************************/
+/**
+* Sets the descriptor ring associated with a device.
+*
+* Once set, the descriptor ring will be associated with the device, even
+* across channel request/free calls. Passing in a NULL descriptor ring
+* will release any descriptor ring currently associated with the device.
+*
+* Note: If you call dma_transfer, or one of the other dma_alloc_ functions
+* the descriptor ring may be released and reallocated.
+*
+* Note: This function will release the descriptor memory for any current
+* descriptor ring associated with this device.
+*
+* @return
+* 0 Descriptors were added successfully
+* -ENODEV Device handed in is invalid.
+*/
+/****************************************************************************/
+
+int dma_set_device_descriptor_ring(DMA_Device_t device, /* Device to update the descriptor ring for. */
+ DMA_DescriptorRing_t *ring /* Descriptor ring to add descriptors to */
+ ) {
+ DMA_DeviceAttribute_t *devAttr;
+
+ if (!IsDeviceValid(device)) {
+ return -ENODEV;
+ }
+ devAttr = &DMA_gDeviceAttribute[device];
+
+ /* Free the previously allocated descriptor ring */
+
+ dma_free_descriptor_ring(&devAttr->ring);
+
+ if (ring != NULL) {
+ /* Copy in the new one */
+
+ devAttr->ring = *ring;
+ }
+
+ /* Set things up so that if dma_transfer is called then this descriptor */
+ /* ring will get freed. */
+
+ devAttr->prevSrcData = 0;
+ devAttr->prevDstData = 0;
+ devAttr->prevNumBytes = 0;
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_set_device_descriptor_ring);
+
+/****************************************************************************/
+/**
+* Retrieves the descriptor ring associated with a device.
+*
+* @return
+* 0 Descriptors were added successfully
+* -ENODEV Device handed in is invalid.
+*/
+/****************************************************************************/
+
+int dma_get_device_descriptor_ring(DMA_Device_t device, /* Device to retrieve the descriptor ring for. */
+ DMA_DescriptorRing_t *ring /* Place to store retrieved ring */
+ ) {
+ DMA_DeviceAttribute_t *devAttr;
+
+ memset(ring, 0, sizeof(*ring));
+
+ if (!IsDeviceValid(device)) {
+ return -ENODEV;
+ }
+ devAttr = &DMA_gDeviceAttribute[device];
+
+ *ring = devAttr->ring;
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_get_device_descriptor_ring);
+
+/****************************************************************************/
+/**
+* Configures a DMA channel.
+*
+* @return
+* >= 0 - Initialization was successfull.
+*
+* -EBUSY - Device is currently being used.
+* -ENODEV - Device handed in is invalid.
+*/
+/****************************************************************************/
+
+static int ConfigChannel(DMA_Handle_t handle)
+{
+ DMA_Channel_t *channel;
+ DMA_DeviceAttribute_t *devAttr;
+ int controllerIdx;
+
+ channel = HandleToChannel(handle);
+ if (channel == NULL) {
+ return -ENODEV;
+ }
+ devAttr = &DMA_gDeviceAttribute[channel->devType];
+ controllerIdx = CONTROLLER_FROM_HANDLE(handle);
+
+ if ((devAttr->flags & DMA_DEVICE_FLAG_PORT_PER_DMAC) != 0) {
+ if (devAttr->config.transferType ==
+ dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL) {
+ devAttr->config.dstPeripheralPort =
+ devAttr->dmacPort[controllerIdx];
+ } else if (devAttr->config.transferType ==
+ dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM) {
+ devAttr->config.srcPeripheralPort =
+ devAttr->dmacPort[controllerIdx];
+ }
+ }
+
+ if (dmacHw_configChannel(channel->dmacHwHandle, &devAttr->config) != 0) {
+ printk(KERN_ERR "ConfigChannel: dmacHw_configChannel failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* Intializes all of the data structures associated with the DMA.
+* @return
+* >= 0 - Initialization was successfull.
+*
+* -EBUSY - Device is currently being used.
+* -ENODEV - Device handed in is invalid.
+*/
+/****************************************************************************/
+
+int dma_init(void)
+{
+ int rc = 0;
+ int controllerIdx;
+ int channelIdx;
+ DMA_Device_t devIdx;
+ DMA_Channel_t *channel;
+ DMA_Handle_t dedicatedHandle;
+
+ memset(&gDMA, 0, sizeof(gDMA));
+
+ init_MUTEX_LOCKED(&gDMA.lock);
+ init_waitqueue_head(&gDMA.freeChannelQ);
+
+ /* Initialize the Hardware */
+
+ dmacHw_initDma();
+
+ /* Start off by marking all of the DMA channels as shared. */
+
+ for (controllerIdx = 0; controllerIdx < DMA_NUM_CONTROLLERS;
+ controllerIdx++) {
+ for (channelIdx = 0; channelIdx < DMA_NUM_CHANNELS;
+ channelIdx++) {
+ channel =
+ &gDMA.controller[controllerIdx].channel[channelIdx];
+
+ channel->flags = 0;
+ channel->devType = DMA_DEVICE_NONE;
+ channel->lastDevType = DMA_DEVICE_NONE;
+
+#if (DMA_DEBUG_TRACK_RESERVATION)
+ channel->fileName = "";
+ channel->lineNum = 0;
+#endif
+
+ channel->dmacHwHandle =
+ dmacHw_getChannelHandle(dmacHw_MAKE_CHANNEL_ID
+ (controllerIdx,
+ channelIdx));
+ dmacHw_initChannel(channel->dmacHwHandle);
+ }
+ }
+
+ /* Record any special attributes that channels may have */
+
+ gDMA.controller[0].channel[0].flags |= DMA_CHANNEL_FLAG_LARGE_FIFO;
+ gDMA.controller[0].channel[1].flags |= DMA_CHANNEL_FLAG_LARGE_FIFO;
+ gDMA.controller[1].channel[0].flags |= DMA_CHANNEL_FLAG_LARGE_FIFO;
+ gDMA.controller[1].channel[1].flags |= DMA_CHANNEL_FLAG_LARGE_FIFO;
+
+ /* Now walk through and record the dedicated channels. */
+
+ for (devIdx = 0; devIdx < DMA_NUM_DEVICE_ENTRIES; devIdx++) {
+ DMA_DeviceAttribute_t *devAttr = &DMA_gDeviceAttribute[devIdx];
+
+ if (((devAttr->flags & DMA_DEVICE_FLAG_NO_ISR) != 0)
+ && ((devAttr->flags & DMA_DEVICE_FLAG_IS_DEDICATED) == 0)) {
+ printk(KERN_ERR
+ "DMA Device: %s Can only request NO_ISR for dedicated devices\n",
+ devAttr->name);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if ((devAttr->flags & DMA_DEVICE_FLAG_IS_DEDICATED) != 0) {
+ /* This is a dedicated device. Mark the channel as being reserved. */
+
+ if (devAttr->dedicatedController >= DMA_NUM_CONTROLLERS) {
+ printk(KERN_ERR
+ "DMA Device: %s DMA Controller %d is out of range\n",
+ devAttr->name,
+ devAttr->dedicatedController);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ if (devAttr->dedicatedChannel >= DMA_NUM_CHANNELS) {
+ printk(KERN_ERR
+ "DMA Device: %s DMA Channel %d is out of range\n",
+ devAttr->name,
+ devAttr->dedicatedChannel);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ dedicatedHandle =
+ MAKE_HANDLE(devAttr->dedicatedController,
+ devAttr->dedicatedChannel);
+ channel = HandleToChannel(dedicatedHandle);
+
+ if ((channel->flags & DMA_CHANNEL_FLAG_IS_DEDICATED) !=
+ 0) {
+ printk
+ ("DMA Device: %s attempting to use same DMA Controller:Channel (%d:%d) as %s\n",
+ devAttr->name,
+ devAttr->dedicatedController,
+ devAttr->dedicatedChannel,
+ DMA_gDeviceAttribute[channel->devType].
+ name);
+ rc = -EBUSY;
+ goto out;
+ }
+
+ channel->flags |= DMA_CHANNEL_FLAG_IS_DEDICATED;
+ channel->devType = devIdx;
+
+ if (devAttr->flags & DMA_DEVICE_FLAG_NO_ISR) {
+ channel->flags |= DMA_CHANNEL_FLAG_NO_ISR;
+ }
+
+ /* For dedicated channels, we can go ahead and configure the DMA channel now */
+ /* as well. */
+
+ ConfigChannel(dedicatedHandle);
+ }
+ }
+
+ /* Go through and register the interrupt handlers */
+
+ for (controllerIdx = 0; controllerIdx < DMA_NUM_CONTROLLERS;
+ controllerIdx++) {
+ for (channelIdx = 0; channelIdx < DMA_NUM_CHANNELS;
+ channelIdx++) {
+ channel =
+ &gDMA.controller[controllerIdx].channel[channelIdx];
+
+ if ((channel->flags & DMA_CHANNEL_FLAG_NO_ISR) == 0) {
+ snprintf(channel->name, sizeof(channel->name),
+ "dma %d:%d %s", controllerIdx,
+ channelIdx,
+ channel->devType ==
+ DMA_DEVICE_NONE ? "" :
+ DMA_gDeviceAttribute[channel->devType].
+ name);
+
+ rc =
+ request_irq(IRQ_DMA0C0 +
+ (controllerIdx *
+ DMA_NUM_CHANNELS) +
+ channelIdx,
+ dma_interrupt_handler,
+ IRQF_DISABLED, channel->name,
+ channel);
+ if (rc != 0) {
+ printk(KERN_ERR
+ "request_irq for IRQ_DMA%dC%d failed\n",
+ controllerIdx, channelIdx);
+ }
+ }
+ }
+ }
+
+ /* Create /proc/dma/channels and /proc/dma/devices */
+
+ gDmaDir = create_proc_entry("dma", S_IFDIR | S_IRUGO | S_IXUGO, NULL);
+
+ if (gDmaDir == NULL) {
+ printk(KERN_ERR "Unable to create /proc/dma\n");
+ } else {
+ create_proc_read_entry("channels", 0, gDmaDir,
+ dma_proc_read_channels, NULL);
+ create_proc_read_entry("devices", 0, gDmaDir,
+ dma_proc_read_devices, NULL);
+ create_proc_read_entry("mem-type", 0, gDmaDir,
+ dma_proc_read_mem_type, NULL);
+ }
+
+out:
+
+ up(&gDMA.lock);
+
+ return rc;
+}
+
+/****************************************************************************/
+/**
+* Reserves a channel for use with @a dev. If the device is setup to use
+* a shared channel, then this function will block until a free channel
+* becomes available.
+*
+* @return
+* >= 0 - A valid DMA Handle.
+* -EBUSY - Device is currently being used.
+* -ENODEV - Device handed in is invalid.
+*/
+/****************************************************************************/
+
+#if (DMA_DEBUG_TRACK_RESERVATION)
+DMA_Handle_t dma_request_channel_dbg
+ (DMA_Device_t dev, const char *fileName, int lineNum)
+#else
+DMA_Handle_t dma_request_channel(DMA_Device_t dev)
+#endif
+{
+ DMA_Handle_t handle;
+ DMA_DeviceAttribute_t *devAttr;
+ DMA_Channel_t *channel;
+ int controllerIdx;
+ int controllerIdx2;
+ int channelIdx;
+
+ if (down_interruptible(&gDMA.lock) < 0) {
+ return -ERESTARTSYS;
+ }
+
+ if ((dev < 0) || (dev >= DMA_NUM_DEVICE_ENTRIES)) {
+ handle = -ENODEV;
+ goto out;
+ }
+ devAttr = &DMA_gDeviceAttribute[dev];
+
+#if (DMA_DEBUG_TRACK_RESERVATION)
+ {
+ char *s;
+
+ s = strrchr(fileName, '/');
+ if (s != NULL) {
+ fileName = s + 1;
+ }
+ }
+#endif
+ if ((devAttr->flags & DMA_DEVICE_FLAG_IN_USE) != 0) {
+ /* This device has already been requested and not been freed */
+
+ printk(KERN_ERR "%s: device %s is already requested\n",
+ __func__, devAttr->name);
+ handle = -EBUSY;
+ goto out;
+ }
+
+ if ((devAttr->flags & DMA_DEVICE_FLAG_IS_DEDICATED) != 0) {
+ /* This device has a dedicated channel. */
+
+ channel =
+ &gDMA.controller[devAttr->dedicatedController].
+ channel[devAttr->dedicatedChannel];
+ if ((channel->flags & DMA_CHANNEL_FLAG_IN_USE) != 0) {
+ handle = -EBUSY;
+ goto out;
+ }
+
+ channel->flags |= DMA_CHANNEL_FLAG_IN_USE;
+ devAttr->flags |= DMA_DEVICE_FLAG_IN_USE;
+
+#if (DMA_DEBUG_TRACK_RESERVATION)
+ channel->fileName = fileName;
+ channel->lineNum = lineNum;
+#endif
+ handle =
+ MAKE_HANDLE(devAttr->dedicatedController,
+ devAttr->dedicatedChannel);
+ goto out;
+ }
+
+ /* This device needs to use one of the shared channels. */
+
+ handle = DMA_INVALID_HANDLE;
+ while (handle == DMA_INVALID_HANDLE) {
+ /* Scan through the shared channels and see if one is available */
+
+ for (controllerIdx2 = 0; controllerIdx2 < DMA_NUM_CONTROLLERS;
+ controllerIdx2++) {
+ /* Check to see if we should try on controller 1 first. */
+
+ controllerIdx = controllerIdx2;
+ if ((devAttr->
+ flags & DMA_DEVICE_FLAG_ALLOC_DMA1_FIRST) != 0) {
+ controllerIdx = 1 - controllerIdx;
+ }
+
+ /* See if the device is available on the controller being tested */
+
+ if ((devAttr->
+ flags & (DMA_DEVICE_FLAG_ON_DMA0 << controllerIdx))
+ != 0) {
+ for (channelIdx = 0;
+ channelIdx < DMA_NUM_CHANNELS;
+ channelIdx++) {
+ channel =
+ &gDMA.controller[controllerIdx].
+ channel[channelIdx];
+
+ if (((channel->
+ flags &
+ DMA_CHANNEL_FLAG_IS_DEDICATED) ==
+ 0)
+ &&
+ ((channel->
+ flags & DMA_CHANNEL_FLAG_IN_USE)
+ == 0)) {
+ if (((channel->
+ flags &
+ DMA_CHANNEL_FLAG_LARGE_FIFO)
+ != 0)
+ &&
+ ((devAttr->
+ flags &
+ DMA_DEVICE_FLAG_ALLOW_LARGE_FIFO)
+ == 0)) {
+ /* This channel is a large fifo - don't tie it up */
+ /* with devices that we don't want using it. */
+
+ continue;
+ }
+
+ channel->flags |=
+ DMA_CHANNEL_FLAG_IN_USE;
+ channel->devType = dev;
+ devAttr->flags |=
+ DMA_DEVICE_FLAG_IN_USE;
+
+#if (DMA_DEBUG_TRACK_RESERVATION)
+ channel->fileName = fileName;
+ channel->lineNum = lineNum;
+#endif
+ handle =
+ MAKE_HANDLE(controllerIdx,
+ channelIdx);
+
+ /* Now that we've reserved the channel - we can go ahead and configure it */
+
+ if (ConfigChannel(handle) != 0) {
+ handle = -EIO;
+ printk(KERN_ERR
+ "dma_request_channel: ConfigChannel failed\n");
+ }
+ goto out;
+ }
+ }
+ }
+ }
+
+ /* No channels are currently available. Let's wait for one to free up. */
+
+ {
+ DEFINE_WAIT(wait);
+
+ prepare_to_wait(&gDMA.freeChannelQ, &wait,
+ TASK_INTERRUPTIBLE);
+ up(&gDMA.lock);
+ schedule();
+ finish_wait(&gDMA.freeChannelQ, &wait);
+
+ if (signal_pending(current)) {
+ /* We don't currently hold gDMA.lock, so we return directly */
+
+ return -ERESTARTSYS;
+ }
+ }
+
+ if (down_interruptible(&gDMA.lock)) {
+ return -ERESTARTSYS;
+ }
+ }
+
+out:
+ up(&gDMA.lock);
+
+ return handle;
+}
+
+/* Create both _dbg and non _dbg functions for modules. */
+
+#if (DMA_DEBUG_TRACK_RESERVATION)
+#undef dma_request_channel
+DMA_Handle_t dma_request_channel(DMA_Device_t dev)
+{
+ return dma_request_channel_dbg(dev, __FILE__, __LINE__);
+}
+
+EXPORT_SYMBOL(dma_request_channel_dbg);
+#endif
+EXPORT_SYMBOL(dma_request_channel);
+
+/****************************************************************************/
+/**
+* Frees a previously allocated DMA Handle.
+*/
+/****************************************************************************/
+
+int dma_free_channel(DMA_Handle_t handle /* DMA handle. */
+ ) {
+ int rc = 0;
+ DMA_Channel_t *channel;
+ DMA_DeviceAttribute_t *devAttr;
+
+ if (down_interruptible(&gDMA.lock) < 0) {
+ return -ERESTARTSYS;
+ }
+
+ channel = HandleToChannel(handle);
+ if (channel == NULL) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ devAttr = &DMA_gDeviceAttribute[channel->devType];
+
+ if ((channel->flags & DMA_CHANNEL_FLAG_IS_DEDICATED) == 0) {
+ channel->lastDevType = channel->devType;
+ channel->devType = DMA_DEVICE_NONE;
+ }
+ channel->flags &= ~DMA_CHANNEL_FLAG_IN_USE;
+ devAttr->flags &= ~DMA_DEVICE_FLAG_IN_USE;
+
+out:
+ up(&gDMA.lock);
+
+ wake_up_interruptible(&gDMA.freeChannelQ);
+
+ return rc;
+}
+
+EXPORT_SYMBOL(dma_free_channel);
+
+/****************************************************************************/
+/**
+* Determines if a given device has been configured as using a shared
+* channel.
+*
+* @return
+* 0 Device uses a dedicated channel
+* > zero Device uses a shared channel
+* < zero Error code
+*/
+/****************************************************************************/
+
+int dma_device_is_channel_shared(DMA_Device_t device /* Device to check. */
+ ) {
+ DMA_DeviceAttribute_t *devAttr;
+
+ if (!IsDeviceValid(device)) {
+ return -ENODEV;
+ }
+ devAttr = &DMA_gDeviceAttribute[device];
+
+ return ((devAttr->flags & DMA_DEVICE_FLAG_IS_DEDICATED) == 0);
+}
+
+EXPORT_SYMBOL(dma_device_is_channel_shared);
+
+/****************************************************************************/
+/**
+* Allocates buffers for the descriptors. This is normally done automatically
+* but needs to be done explicitly when initiating a dma from interrupt
+* context.
+*
+* @return
+* 0 Descriptors were allocated successfully
+* -EINVAL Invalid device type for this kind of transfer
+* (i.e. the device is _MEM_TO_DEV and not _DEV_TO_MEM)
+* -ENOMEM Memory exhausted
+*/
+/****************************************************************************/
+
+int dma_alloc_descriptors(DMA_Handle_t handle, /* DMA Handle */
+ dmacHw_TRANSFER_TYPE_e transferType, /* Type of transfer being performed */
+ dma_addr_t srcData, /* Place to get data to write to device */
+ dma_addr_t dstData, /* Pointer to device data address */
+ size_t numBytes /* Number of bytes to transfer to the device */
+ ) {
+ DMA_Channel_t *channel;
+ DMA_DeviceAttribute_t *devAttr;
+ int numDescriptors;
+ size_t ringBytesRequired;
+ int rc = 0;
+
+ channel = HandleToChannel(handle);
+ if (channel == NULL) {
+ return -ENODEV;
+ }
+
+ devAttr = &DMA_gDeviceAttribute[channel->devType];
+
+ if (devAttr->config.transferType != transferType) {
+ return -EINVAL;
+ }
+
+ /* Figure out how many descriptors we need. */
+
+ /* printk("srcData: 0x%08x dstData: 0x%08x, numBytes: %d\n", */
+ /* srcData, dstData, numBytes); */
+
+ numDescriptors = dmacHw_calculateDescriptorCount(&devAttr->config,
+ (void *)srcData,
+ (void *)dstData,
+ numBytes);
+ if (numDescriptors < 0) {
+ printk(KERN_ERR "%s: dmacHw_calculateDescriptorCount failed\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Check to see if we can reuse the existing descriptor ring, or if we need to allocate */
+ /* a new one. */
+
+ ringBytesRequired = dmacHw_descriptorLen(numDescriptors);
+
+ /* printk("ringBytesRequired: %d\n", ringBytesRequired); */
+
+ if (ringBytesRequired > devAttr->ring.bytesAllocated) {
+ /* Make sure that this code path is never taken from interrupt context. */
+ /* It's OK for an interrupt to initiate a DMA transfer, but the descriptor */
+ /* allocation needs to have already been done. */
+
+ might_sleep();
+
+ /* Free the old descriptor ring and allocate a new one. */
+
+ dma_free_descriptor_ring(&devAttr->ring);
+
+ /* And allocate a new one. */
+
+ rc =
+ dma_alloc_descriptor_ring(&devAttr->ring,
+ numDescriptors);
+ if (rc < 0) {
+ printk(KERN_ERR
+ "%s: dma_alloc_descriptor_ring(%d) failed\n",
+ __func__, numDescriptors);
+ return rc;
+ }
+ /* Setup the descriptor for this transfer */
+
+ if (dmacHw_initDescriptor(devAttr->ring.virtAddr,
+ devAttr->ring.physAddr,
+ devAttr->ring.bytesAllocated,
+ numDescriptors) < 0) {
+ printk(KERN_ERR "%s: dmacHw_initDescriptor failed\n",
+ __func__);
+ return -EINVAL;
+ }
+ } else {
+ /* We've already got enough ring buffer allocated. All we need to do is reset */
+ /* any control information, just in case the previous DMA was stopped. */
+
+ dmacHw_resetDescriptorControl(devAttr->ring.virtAddr);
+ }
+
+ /* dma_alloc/free both set the prevSrc/DstData to 0. If they happen to be the same */
+ /* as last time, then we don't need to call setDataDescriptor again. */
+
+ if (dmacHw_setDataDescriptor(&devAttr->config,
+ devAttr->ring.virtAddr,
+ (void *)srcData,
+ (void *)dstData, numBytes) < 0) {
+ printk(KERN_ERR "%s: dmacHw_setDataDescriptor failed\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* Remember the critical information for this transfer so that we can eliminate */
+ /* another call to dma_alloc_descriptors if the caller reuses the same buffers */
+
+ devAttr->prevSrcData = srcData;
+ devAttr->prevDstData = dstData;
+ devAttr->prevNumBytes = numBytes;
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_alloc_descriptors);
+
+/****************************************************************************/
+/**
+* Allocates and sets up descriptors for a double buffered circular buffer.
+*
+* This is primarily intended to be used for things like the ingress samples
+* from a microphone.
+*
+* @return
+* > 0 Number of descriptors actually allocated.
+* -EINVAL Invalid device type for this kind of transfer
+* (i.e. the device is _MEM_TO_DEV and not _DEV_TO_MEM)
+* -ENOMEM Memory exhausted
+*/
+/****************************************************************************/
+
+int dma_alloc_double_dst_descriptors(DMA_Handle_t handle, /* DMA Handle */
+ dma_addr_t srcData, /* Physical address of source data */
+ dma_addr_t dstData1, /* Physical address of first destination buffer */
+ dma_addr_t dstData2, /* Physical address of second destination buffer */
+ size_t numBytes /* Number of bytes in each destination buffer */
+ ) {
+ DMA_Channel_t *channel;
+ DMA_DeviceAttribute_t *devAttr;
+ int numDst1Descriptors;
+ int numDst2Descriptors;
+ int numDescriptors;
+ size_t ringBytesRequired;
+ int rc = 0;
+
+ channel = HandleToChannel(handle);
+ if (channel == NULL) {
+ return -ENODEV;
+ }
+
+ devAttr = &DMA_gDeviceAttribute[channel->devType];
+
+ /* Figure out how many descriptors we need. */
+
+ /* printk("srcData: 0x%08x dstData: 0x%08x, numBytes: %d\n", */
+ /* srcData, dstData, numBytes); */
+
+ numDst1Descriptors =
+ dmacHw_calculateDescriptorCount(&devAttr->config, (void *)srcData,
+ (void *)dstData1, numBytes);
+ if (numDst1Descriptors < 0) {
+ return -EINVAL;
+ }
+ numDst2Descriptors =
+ dmacHw_calculateDescriptorCount(&devAttr->config, (void *)srcData,
+ (void *)dstData2, numBytes);
+ if (numDst2Descriptors < 0) {
+ return -EINVAL;
+ }
+ numDescriptors = numDst1Descriptors + numDst2Descriptors;
+ /* printk("numDescriptors: %d\n", numDescriptors); */
+
+ /* Check to see if we can reuse the existing descriptor ring, or if we need to allocate */
+ /* a new one. */
+
+ ringBytesRequired = dmacHw_descriptorLen(numDescriptors);
+
+ /* printk("ringBytesRequired: %d\n", ringBytesRequired); */
+
+ if (ringBytesRequired > devAttr->ring.bytesAllocated) {
+ /* Make sure that this code path is never taken from interrupt context. */
+ /* It's OK for an interrupt to initiate a DMA transfer, but the descriptor */
+ /* allocation needs to have already been done. */
+
+ might_sleep();
+
+ /* Free the old descriptor ring and allocate a new one. */
+
+ dma_free_descriptor_ring(&devAttr->ring);
+
+ /* And allocate a new one. */
+
+ rc =
+ dma_alloc_descriptor_ring(&devAttr->ring,
+ numDescriptors);
+ if (rc < 0) {
+ printk(KERN_ERR
+ "%s: dma_alloc_descriptor_ring(%d) failed\n",
+ __func__, ringBytesRequired);
+ return rc;
+ }
+ }
+
+ /* Setup the descriptor for this transfer. Since this function is used with */
+ /* CONTINUOUS DMA operations, we need to reinitialize every time, otherwise */
+ /* setDataDescriptor will keep trying to append onto the end. */
+
+ if (dmacHw_initDescriptor(devAttr->ring.virtAddr,
+ devAttr->ring.physAddr,
+ devAttr->ring.bytesAllocated,
+ numDescriptors) < 0) {
+ printk(KERN_ERR "%s: dmacHw_initDescriptor failed\n", __func__);
+ return -EINVAL;
+ }
+
+ /* dma_alloc/free both set the prevSrc/DstData to 0. If they happen to be the same */
+ /* as last time, then we don't need to call setDataDescriptor again. */
+
+ if (dmacHw_setDataDescriptor(&devAttr->config,
+ devAttr->ring.virtAddr,
+ (void *)srcData,
+ (void *)dstData1, numBytes) < 0) {
+ printk(KERN_ERR "%s: dmacHw_setDataDescriptor 1 failed\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (dmacHw_setDataDescriptor(&devAttr->config,
+ devAttr->ring.virtAddr,
+ (void *)srcData,
+ (void *)dstData2, numBytes) < 0) {
+ printk(KERN_ERR "%s: dmacHw_setDataDescriptor 2 failed\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* You should use dma_start_transfer rather than dma_transfer_xxx so we don't */
+ /* try to make the 'prev' variables right. */
+
+ devAttr->prevSrcData = 0;
+ devAttr->prevDstData = 0;
+ devAttr->prevNumBytes = 0;
+
+ return numDescriptors;
+}
+
+EXPORT_SYMBOL(dma_alloc_double_dst_descriptors);
+
+/****************************************************************************/
+/**
+* Initiates a transfer when the descriptors have already been setup.
+*
+* This is a special case, and normally, the dma_transfer_xxx functions should
+* be used.
+*
+* @return
+* 0 Transfer was started successfully
+* -ENODEV Invalid handle
+*/
+/****************************************************************************/
+
+int dma_start_transfer(DMA_Handle_t handle)
+{
+ DMA_Channel_t *channel;
+ DMA_DeviceAttribute_t *devAttr;
+
+ channel = HandleToChannel(handle);
+ if (channel == NULL) {
+ return -ENODEV;
+ }
+ devAttr = &DMA_gDeviceAttribute[channel->devType];
+
+ dmacHw_initiateTransfer(channel->dmacHwHandle, &devAttr->config,
+ devAttr->ring.virtAddr);
+
+ /* Since we got this far, everything went successfully */
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_start_transfer);
+
+/****************************************************************************/
+/**
+* Stops a previously started DMA transfer.
+*
+* @return
+* 0 Transfer was stopped successfully
+* -ENODEV Invalid handle
+*/
+/****************************************************************************/
+
+int dma_stop_transfer(DMA_Handle_t handle)
+{
+ DMA_Channel_t *channel;
+
+ channel = HandleToChannel(handle);
+ if (channel == NULL) {
+ return -ENODEV;
+ }
+
+ dmacHw_stopTransfer(channel->dmacHwHandle);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_stop_transfer);
+
+/****************************************************************************/
+/**
+* Waits for a DMA to complete by polling. This function is only intended
+* to be used for testing. Interrupts should be used for most DMA operations.
+*/
+/****************************************************************************/
+
+int dma_wait_transfer_done(DMA_Handle_t handle)
+{
+ DMA_Channel_t *channel;
+ dmacHw_TRANSFER_STATUS_e status;
+
+ channel = HandleToChannel(handle);
+ if (channel == NULL) {
+ return -ENODEV;
+ }
+
+ while ((status =
+ dmacHw_transferCompleted(channel->dmacHwHandle)) ==
+ dmacHw_TRANSFER_STATUS_BUSY) {
+ ;
+ }
+
+ if (status == dmacHw_TRANSFER_STATUS_ERROR) {
+ printk(KERN_ERR "%s: DMA transfer failed\n", __func__);
+ return -EIO;
+ }
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_wait_transfer_done);
+
+/****************************************************************************/
+/**
+* Initiates a DMA, allocating the descriptors as required.
+*
+* @return
+* 0 Transfer was started successfully
+* -EINVAL Invalid device type for this kind of transfer
+* (i.e. the device is _DEV_TO_MEM and not _MEM_TO_DEV)
+*/
+/****************************************************************************/
+
+int dma_transfer(DMA_Handle_t handle, /* DMA Handle */
+ dmacHw_TRANSFER_TYPE_e transferType, /* Type of transfer being performed */
+ dma_addr_t srcData, /* Place to get data to write to device */
+ dma_addr_t dstData, /* Pointer to device data address */
+ size_t numBytes /* Number of bytes to transfer to the device */
+ ) {
+ DMA_Channel_t *channel;
+ DMA_DeviceAttribute_t *devAttr;
+ int rc = 0;
+
+ channel = HandleToChannel(handle);
+ if (channel == NULL) {
+ return -ENODEV;
+ }
+
+ devAttr = &DMA_gDeviceAttribute[channel->devType];
+
+ if (devAttr->config.transferType != transferType) {
+ return -EINVAL;
+ }
+
+ /* We keep track of the information about the previous request for this */
+ /* device, and if the attributes match, then we can use the descriptors we setup */
+ /* the last time, and not have to reinitialize everything. */
+
+ {
+ rc =
+ dma_alloc_descriptors(handle, transferType, srcData,
+ dstData, numBytes);
+ if (rc != 0) {
+ return rc;
+ }
+ }
+
+ /* And kick off the transfer */
+
+ devAttr->numBytes = numBytes;
+ devAttr->transferStartTime = timer_get_tick_count();
+
+ dmacHw_initiateTransfer(channel->dmacHwHandle, &devAttr->config,
+ devAttr->ring.virtAddr);
+
+ /* Since we got this far, everything went successfully */
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_transfer);
+
+/****************************************************************************/
+/**
+* Set the callback function which will be called when a transfer completes.
+* If a NULL callback function is set, then no callback will occur.
+*
+* @note @a devHandler will be called from IRQ context.
+*
+* @return
+* 0 - Success
+* -ENODEV - Device handed in is invalid.
+*/
+/****************************************************************************/
+
+int dma_set_device_handler(DMA_Device_t dev, /* Device to set the callback for. */
+ DMA_DeviceHandler_t devHandler, /* Function to call when the DMA completes */
+ void *userData /* Pointer which will be passed to devHandler. */
+ ) {
+ DMA_DeviceAttribute_t *devAttr;
+ unsigned long flags;
+
+ if (!IsDeviceValid(dev)) {
+ return -ENODEV;
+ }
+ devAttr = &DMA_gDeviceAttribute[dev];
+
+ local_irq_save(flags);
+
+ devAttr->userData = userData;
+ devAttr->devHandler = devHandler;
+
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_set_device_handler);
+
+/****************************************************************************/
+/**
+* Initializes a memory mapping structure
+*/
+/****************************************************************************/
+
+int dma_init_mem_map(DMA_MemMap_t *memMap)
+{
+ memset(memMap, 0, sizeof(*memMap));
+
+ init_MUTEX(&memMap->lock);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_init_mem_map);
+
+/****************************************************************************/
+/**
+* Releases any memory currently being held by a memory mapping structure.
+*/
+/****************************************************************************/
+
+int dma_term_mem_map(DMA_MemMap_t *memMap)
+{
+ down(&memMap->lock); /* Just being paranoid */
+
+ /* Free up any allocated memory */
+
+ up(&memMap->lock);
+ memset(memMap, 0, sizeof(*memMap));
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_term_mem_map);
+
+/****************************************************************************/
+/**
+* Looks at a memory address and categorizes it.
+*
+* @return One of the values from the DMA_MemType_t enumeration.
+*/
+/****************************************************************************/
+
+DMA_MemType_t dma_mem_type(void *addr)
+{
+ unsigned long addrVal = (unsigned long)addr;
+
+ if (addrVal >= VMALLOC_END) {
+ /* NOTE: DMA virtual memory space starts at 0xFFxxxxxx */
+
+ /* dma_alloc_xxx pages are physically and virtually contiguous */
+
+ return DMA_MEM_TYPE_DMA;
+ }
+
+ /* Technically, we could add one more classification. Addresses between VMALLOC_END */
+ /* and the beginning of the DMA virtual address could be considered to be I/O space. */
+ /* Right now, nobody cares about this particular classification, so we ignore it. */
+
+ if (is_vmalloc_addr(addr)) {
+ /* Address comes from the vmalloc'd region. Pages are virtually */
+ /* contiguous but NOT physically contiguous */
+
+ return DMA_MEM_TYPE_VMALLOC;
+ }
+
+ if (addrVal >= PAGE_OFFSET) {
+ /* PAGE_OFFSET is typically 0xC0000000 */
+
+ /* kmalloc'd pages are physically contiguous */
+
+ return DMA_MEM_TYPE_KMALLOC;
+ }
+
+ return DMA_MEM_TYPE_USER;
+}
+
+EXPORT_SYMBOL(dma_mem_type);
+
+/****************************************************************************/
+/**
+* Looks at a memory address and determines if we support DMA'ing to/from
+* that type of memory.
+*
+* @return boolean -
+* return value != 0 means dma supported
+* return value == 0 means dma not supported
+*/
+/****************************************************************************/
+
+int dma_mem_supports_dma(void *addr)
+{
+ DMA_MemType_t memType = dma_mem_type(addr);
+
+ return (memType == DMA_MEM_TYPE_DMA)
+#if ALLOW_MAP_OF_KMALLOC_MEMORY
+ || (memType == DMA_MEM_TYPE_KMALLOC)
+#endif
+ || (memType == DMA_MEM_TYPE_USER);
+}
+
+EXPORT_SYMBOL(dma_mem_supports_dma);
+
+/****************************************************************************/
+/**
+* Maps in a memory region such that it can be used for performing a DMA.
+*
+* @return
+*/
+/****************************************************************************/
+
+int dma_map_start(DMA_MemMap_t *memMap, /* Stores state information about the map */
+ enum dma_data_direction dir /* Direction that the mapping will be going */
+ ) {
+ int rc;
+
+ down(&memMap->lock);
+
+ DMA_MAP_PRINT("memMap: %p\n", memMap);
+
+ if (memMap->inUse) {
+ printk(KERN_ERR "%s: memory map %p is already being used\n",
+ __func__, memMap);
+ rc = -EBUSY;
+ goto out;
+ }
+
+ memMap->inUse = 1;
+ memMap->dir = dir;
+ memMap->numRegionsUsed = 0;
+
+ rc = 0;
+
+out:
+
+ DMA_MAP_PRINT("returning %d", rc);
+
+ up(&memMap->lock);
+
+ return rc;
+}
+
+EXPORT_SYMBOL(dma_map_start);
+
+/****************************************************************************/
+/**
+* Adds a segment of memory to a memory map. Each segment is both
+* physically and virtually contiguous.
+*
+* @return 0 on success, error code otherwise.
+*/
+/****************************************************************************/
+
+static int dma_map_add_segment(DMA_MemMap_t *memMap, /* Stores state information about the map */
+ DMA_Region_t *region, /* Region that the segment belongs to */
+ void *virtAddr, /* Virtual address of the segment being added */
+ dma_addr_t physAddr, /* Physical address of the segment being added */
+ size_t numBytes /* Number of bytes of the segment being added */
+ ) {
+ DMA_Segment_t *segment;
+
+ DMA_MAP_PRINT("memMap:%p va:%p pa:0x%x #:%d\n", memMap, virtAddr,
+ physAddr, numBytes);
+
+ /* Sanity check */
+
+ if (((unsigned long)virtAddr < (unsigned long)region->virtAddr)
+ || (((unsigned long)virtAddr + numBytes)) >
+ ((unsigned long)region->virtAddr + region->numBytes)) {
+ printk(KERN_ERR
+ "%s: virtAddr %p is outside region @ %p len: %d\n",
+ __func__, virtAddr, region->virtAddr, region->numBytes);
+ return -EINVAL;
+ }
+
+ if (region->numSegmentsUsed > 0) {
+ /* Check to see if this segment is physically contiguous with the previous one */
+
+ segment = &region->segment[region->numSegmentsUsed - 1];
+
+ if ((segment->physAddr + segment->numBytes) == physAddr) {
+ /* It is - just add on to the end */
+
+ DMA_MAP_PRINT("appending %d bytes to last segment\n",
+ numBytes);
+
+ segment->numBytes += numBytes;
+
+ return 0;
+ }
+ }
+
+ /* Reallocate to hold more segments, if required. */
+
+ if (region->numSegmentsUsed >= region->numSegmentsAllocated) {
+ DMA_Segment_t *newSegment;
+ size_t oldSize =
+ region->numSegmentsAllocated * sizeof(*newSegment);
+ int newAlloc = region->numSegmentsAllocated + 4;
+ size_t newSize = newAlloc * sizeof(*newSegment);
+
+ newSegment = kmalloc(newSize, GFP_KERNEL);
+ if (newSegment == NULL) {
+ return -ENOMEM;
+ }
+ memcpy(newSegment, region->segment, oldSize);
+ memset(&((uint8_t *) newSegment)[oldSize], 0,
+ newSize - oldSize);
+ kfree(region->segment);
+
+ region->numSegmentsAllocated = newAlloc;
+ region->segment = newSegment;
+ }
+
+ segment = &region->segment[region->numSegmentsUsed];
+ region->numSegmentsUsed++;
+
+ segment->virtAddr = virtAddr;
+ segment->physAddr = physAddr;
+ segment->numBytes = numBytes;
+
+ DMA_MAP_PRINT("returning success\n");
+
+ return 0;
+}
+
+/****************************************************************************/
+/**
+* Adds a region of memory to a memory map. Each region is virtually
+* contiguous, but not necessarily physically contiguous.
+*
+* @return 0 on success, error code otherwise.
+*/
+/****************************************************************************/
+
+int dma_map_add_region(DMA_MemMap_t *memMap, /* Stores state information about the map */
+ void *mem, /* Virtual address that we want to get a map of */
+ size_t numBytes /* Number of bytes being mapped */
+ ) {
+ unsigned long addr = (unsigned long)mem;
+ unsigned int offset;
+ int rc = 0;
+ DMA_Region_t *region;
+ dma_addr_t physAddr;
+
+ down(&memMap->lock);
+
+ DMA_MAP_PRINT("memMap:%p va:%p #:%d\n", memMap, mem, numBytes);
+
+ if (!memMap->inUse) {
+ printk(KERN_ERR "%s: Make sure you call dma_map_start first\n",
+ __func__);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* Reallocate to hold more regions. */
+
+ if (memMap->numRegionsUsed >= memMap->numRegionsAllocated) {
+ DMA_Region_t *newRegion;
+ size_t oldSize =
+ memMap->numRegionsAllocated * sizeof(*newRegion);
+ int newAlloc = memMap->numRegionsAllocated + 4;
+ size_t newSize = newAlloc * sizeof(*newRegion);
+
+ newRegion = kmalloc(newSize, GFP_KERNEL);
+ if (newRegion == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ memcpy(newRegion, memMap->region, oldSize);
+ memset(&((uint8_t *) newRegion)[oldSize], 0, newSize - oldSize);
+
+ kfree(memMap->region);
+
+ memMap->numRegionsAllocated = newAlloc;
+ memMap->region = newRegion;
+ }
+
+ region = &memMap->region[memMap->numRegionsUsed];
+ memMap->numRegionsUsed++;
+
+ offset = addr & ~PAGE_MASK;
+
+ region->memType = dma_mem_type(mem);
+ region->virtAddr = mem;
+ region->numBytes = numBytes;
+ region->numSegmentsUsed = 0;
+ region->numLockedPages = 0;
+ region->lockedPages = NULL;
+
+ switch (region->memType) {
+ case DMA_MEM_TYPE_VMALLOC:
+ {
+ atomic_inc(&gDmaStatMemTypeVmalloc);
+
+ /* printk(KERN_ERR "%s: vmalloc'd pages are not supported\n", __func__); */
+
+ /* vmalloc'd pages are not physically contiguous */
+
+ rc = -EINVAL;
+ break;
+ }
+
+ case DMA_MEM_TYPE_KMALLOC:
+ {
+ atomic_inc(&gDmaStatMemTypeKmalloc);
+
+ /* kmalloc'd pages are physically contiguous, so they'll have exactly */
+ /* one segment */
+
+#if ALLOW_MAP_OF_KMALLOC_MEMORY
+ physAddr =
+ dma_map_single(NULL, mem, numBytes, memMap->dir);
+ rc = dma_map_add_segment(memMap, region, mem, physAddr,
+ numBytes);
+#else
+ rc = -EINVAL;
+#endif
+ break;
+ }
+
+ case DMA_MEM_TYPE_DMA:
+ {
+ /* dma_alloc_xxx pages are physically contiguous */
+
+ atomic_inc(&gDmaStatMemTypeCoherent);
+
+ physAddr = (vmalloc_to_pfn(mem) << PAGE_SHIFT) + offset;
+
+ dma_sync_single_for_cpu(NULL, physAddr, numBytes,
+ memMap->dir);
+ rc = dma_map_add_segment(memMap, region, mem, physAddr,
+ numBytes);
+ break;
+ }
+
+ case DMA_MEM_TYPE_USER:
+ {
+ size_t firstPageOffset;
+ size_t firstPageSize;
+ struct page **pages;
+ struct task_struct *userTask;
+
+ atomic_inc(&gDmaStatMemTypeUser);
+
+#if 1
+ /* If the pages are user pages, then the dma_mem_map_set_user_task function */
+ /* must have been previously called. */
+
+ if (memMap->userTask == NULL) {
+ printk(KERN_ERR
+ "%s: must call dma_mem_map_set_user_task when using user-mode memory\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ /* User pages need to be locked. */
+
+ firstPageOffset =
+ (unsigned long)region->virtAddr & (PAGE_SIZE - 1);
+ firstPageSize = PAGE_SIZE - firstPageOffset;
+
+ region->numLockedPages = (firstPageOffset
+ + region->numBytes +
+ PAGE_SIZE - 1) / PAGE_SIZE;
+ pages =
+ kmalloc(region->numLockedPages *
+ sizeof(struct page *), GFP_KERNEL);
+
+ if (pages == NULL) {
+ region->numLockedPages = 0;
+ return -ENOMEM;
+ }
+
+ userTask = memMap->userTask;
+
+ down_read(&userTask->mm->mmap_sem);
+ rc = get_user_pages(userTask, /* task */
+ userTask->mm, /* mm */
+ (unsigned long)region->virtAddr, /* start */
+ region->numLockedPages, /* len */
+ memMap->dir == DMA_FROM_DEVICE, /* write */
+ 0, /* force */
+ pages, /* pages (array of pointers to page) */
+ NULL); /* vmas */
+ up_read(&userTask->mm->mmap_sem);
+
+ if (rc != region->numLockedPages) {
+ kfree(pages);
+ region->numLockedPages = 0;
+
+ if (rc >= 0) {
+ rc = -EINVAL;
+ }
+ } else {
+ uint8_t *virtAddr = region->virtAddr;
+ size_t bytesRemaining;
+ int pageIdx;
+
+ rc = 0; /* Since get_user_pages returns +ve number */
+
+ region->lockedPages = pages;
+
+ /* We've locked the user pages. Now we need to walk them and figure */
+ /* out the physical addresses. */
+
+ /* The first page may be partial */
+
+ dma_map_add_segment(memMap,
+ region,
+ virtAddr,
+ PFN_PHYS(page_to_pfn
+ (pages[0])) +
+ firstPageOffset,
+ firstPageSize);
+
+ virtAddr += firstPageSize;
+ bytesRemaining =
+ region->numBytes - firstPageSize;
+
+ for (pageIdx = 1;
+ pageIdx < region->numLockedPages;
+ pageIdx++) {
+ size_t bytesThisPage =
+ (bytesRemaining >
+ PAGE_SIZE ? PAGE_SIZE :
+ bytesRemaining);
+
+ DMA_MAP_PRINT
+ ("pageIdx:%d pages[pageIdx]=%p pfn=%u phys=%u\n",
+ pageIdx, pages[pageIdx],
+ page_to_pfn(pages[pageIdx]),
+ PFN_PHYS(page_to_pfn
+ (pages[pageIdx])));
+
+ dma_map_add_segment(memMap,
+ region,
+ virtAddr,
+ PFN_PHYS(page_to_pfn
+ (pages
+ [pageIdx])),
+ bytesThisPage);
+
+ virtAddr += bytesThisPage;
+ bytesRemaining -= bytesThisPage;
+ }
+ }
+#else
+ printk(KERN_ERR
+ "%s: User mode pages are not yet supported\n",
+ __func__);
+
+ /* user pages are not physically contiguous */
+
+ rc = -EINVAL;
+#endif
+ break;
+ }
+
+ default:
+ {
+ printk(KERN_ERR "%s: Unsupported memory type: %d\n",
+ __func__, region->memType);
+
+ rc = -EINVAL;
+ break;
+ }
+ }
+
+ if (rc != 0) {
+ memMap->numRegionsUsed--;
+ }
+
+out:
+
+ DMA_MAP_PRINT("returning %d\n", rc);
+
+ up(&memMap->lock);
+
+ return rc;
+}
+
+EXPORT_SYMBOL(dma_map_add_segment);
+
+/****************************************************************************/
+/**
+* Maps in a memory region such that it can be used for performing a DMA.
+*
+* @return 0 on success, error code otherwise.
+*/
+/****************************************************************************/
+
+int dma_map_mem(DMA_MemMap_t *memMap, /* Stores state information about the map */
+ void *mem, /* Virtual address that we want to get a map of */
+ size_t numBytes, /* Number of bytes being mapped */
+ enum dma_data_direction dir /* Direction that the mapping will be going */
+ ) {
+ int rc;
+
+ rc = dma_map_start(memMap, dir);
+ if (rc == 0) {
+ rc = dma_map_add_region(memMap, mem, numBytes);
+ if (rc < 0) {
+ /* Since the add fails, this function will fail, and the caller won't */
+ /* call unmap, so we need to do it here. */
+
+ dma_unmap(memMap, 0);
+ }
+ }
+
+ return rc;
+}
+
+EXPORT_SYMBOL(dma_map_mem);
+
+/****************************************************************************/
+/**
+* Setup a descriptor ring for a given memory map.
+*
+* It is assumed that the descriptor ring has already been initialized, and
+* this routine will only reallocate a new descriptor ring if the existing
+* one is too small.
+*
+* @return 0 on success, error code otherwise.
+*/
+/****************************************************************************/
+
+int dma_map_create_descriptor_ring(DMA_Device_t dev, /* DMA device (where the ring is stored) */
+ DMA_MemMap_t *memMap, /* Memory map that will be used */
+ dma_addr_t devPhysAddr /* Physical address of device */
+ ) {
+ int rc;
+ int numDescriptors;
+ DMA_DeviceAttribute_t *devAttr;
+ DMA_Region_t *region;
+ DMA_Segment_t *segment;
+ dma_addr_t srcPhysAddr;
+ dma_addr_t dstPhysAddr;
+ int regionIdx;
+ int segmentIdx;
+
+ devAttr = &DMA_gDeviceAttribute[dev];
+
+ down(&memMap->lock);
+
+ /* Figure out how many descriptors we need */
+
+ numDescriptors = 0;
+ for (regionIdx = 0; regionIdx < memMap->numRegionsUsed; regionIdx++) {
+ region = &memMap->region[regionIdx];
+
+ for (segmentIdx = 0; segmentIdx < region->numSegmentsUsed;
+ segmentIdx++) {
+ segment = &region->segment[segmentIdx];
+
+ if (memMap->dir == DMA_TO_DEVICE) {
+ srcPhysAddr = segment->physAddr;
+ dstPhysAddr = devPhysAddr;
+ } else {
+ srcPhysAddr = devPhysAddr;
+ dstPhysAddr = segment->physAddr;
+ }
+
+ rc =
+ dma_calculate_descriptor_count(dev, srcPhysAddr,
+ dstPhysAddr,
+ segment->
+ numBytes);
+ if (rc < 0) {
+ printk(KERN_ERR
+ "%s: dma_calculate_descriptor_count failed: %d\n",
+ __func__, rc);
+ goto out;
+ }
+ numDescriptors += rc;
+ }
+ }
+
+ /* Adjust the size of the ring, if it isn't big enough */
+
+ if (numDescriptors > devAttr->ring.descriptorsAllocated) {
+ dma_free_descriptor_ring(&devAttr->ring);
+ rc =
+ dma_alloc_descriptor_ring(&devAttr->ring,
+ numDescriptors);
+ if (rc < 0) {
+ printk(KERN_ERR
+ "%s: dma_alloc_descriptor_ring failed: %d\n",
+ __func__, rc);
+ goto out;
+ }
+ } else {
+ rc =
+ dma_init_descriptor_ring(&devAttr->ring,
+ numDescriptors);
+ if (rc < 0) {
+ printk(KERN_ERR
+ "%s: dma_init_descriptor_ring failed: %d\n",
+ __func__, rc);
+ goto out;
+ }
+ }
+
+ /* Populate the descriptors */
+
+ for (regionIdx = 0; regionIdx < memMap->numRegionsUsed; regionIdx++) {
+ region = &memMap->region[regionIdx];
+
+ for (segmentIdx = 0; segmentIdx < region->numSegmentsUsed;
+ segmentIdx++) {
+ segment = &region->segment[segmentIdx];
+
+ if (memMap->dir == DMA_TO_DEVICE) {
+ srcPhysAddr = segment->physAddr;
+ dstPhysAddr = devPhysAddr;
+ } else {
+ srcPhysAddr = devPhysAddr;
+ dstPhysAddr = segment->physAddr;
+ }
+
+ rc =
+ dma_add_descriptors(&devAttr->ring, dev,
+ srcPhysAddr, dstPhysAddr,
+ segment->numBytes);
+ if (rc < 0) {
+ printk(KERN_ERR
+ "%s: dma_add_descriptors failed: %d\n",
+ __func__, rc);
+ goto out;
+ }
+ }
+ }
+
+ rc = 0;
+
+out:
+
+ up(&memMap->lock);
+ return rc;
+}
+
+EXPORT_SYMBOL(dma_map_create_descriptor_ring);
+
+/****************************************************************************/
+/**
+* Maps in a memory region such that it can be used for performing a DMA.
+*
+* @return
+*/
+/****************************************************************************/
+
+int dma_unmap(DMA_MemMap_t *memMap, /* Stores state information about the map */
+ int dirtied /* non-zero if any of the pages were modified */
+ ) {
+ int regionIdx;
+ int segmentIdx;
+ DMA_Region_t *region;
+ DMA_Segment_t *segment;
+
+ for (regionIdx = 0; regionIdx < memMap->numRegionsUsed; regionIdx++) {
+ region = &memMap->region[regionIdx];
+
+ for (segmentIdx = 0; segmentIdx < region->numSegmentsUsed;
+ segmentIdx++) {
+ segment = &region->segment[segmentIdx];
+
+ switch (region->memType) {
+ case DMA_MEM_TYPE_VMALLOC:
+ {
+ printk(KERN_ERR
+ "%s: vmalloc'd pages are not yet supported\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ case DMA_MEM_TYPE_KMALLOC:
+ {
+#if ALLOW_MAP_OF_KMALLOC_MEMORY
+ dma_unmap_single(NULL,
+ segment->physAddr,
+ segment->numBytes,
+ memMap->dir);
+#endif
+ break;
+ }
+
+ case DMA_MEM_TYPE_DMA:
+ {
+ dma_sync_single_for_cpu(NULL,
+ segment->
+ physAddr,
+ segment->
+ numBytes,
+ memMap->dir);
+ break;
+ }
+
+ case DMA_MEM_TYPE_USER:
+ {
+ /* Nothing to do here. */
+
+ break;
+ }
+
+ default:
+ {
+ printk(KERN_ERR
+ "%s: Unsupported memory type: %d\n",
+ __func__, region->memType);
+ return -EINVAL;
+ }
+ }
+
+ segment->virtAddr = NULL;
+ segment->physAddr = 0;
+ segment->numBytes = 0;
+ }
+
+ if (region->numLockedPages > 0) {
+ int pageIdx;
+
+ /* Some user pages were locked. We need to go and unlock them now. */
+
+ for (pageIdx = 0; pageIdx < region->numLockedPages;
+ pageIdx++) {
+ struct page *page =
+ region->lockedPages[pageIdx];
+
+ if (memMap->dir == DMA_FROM_DEVICE) {
+ SetPageDirty(page);
+ }
+ page_cache_release(page);
+ }
+ kfree(region->lockedPages);
+ region->numLockedPages = 0;
+ region->lockedPages = NULL;
+ }
+
+ region->memType = DMA_MEM_TYPE_NONE;
+ region->virtAddr = NULL;
+ region->numBytes = 0;
+ region->numSegmentsUsed = 0;
+ }
+ memMap->userTask = NULL;
+ memMap->numRegionsUsed = 0;
+ memMap->inUse = 0;
+
+ up(&memMap->lock);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(dma_unmap);
diff --git a/arch/arm/mach-bcmring/dma_device.c b/arch/arm/mach-bcmring/dma_device.c
new file mode 100644
index 00000000000..ca0ad736870
--- /dev/null
+++ b/arch/arm/mach-bcmring/dma_device.c
@@ -0,0 +1,593 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file dma_device.c
+*
+* @brief private array of DMA_DeviceAttribute_t
+*/
+/****************************************************************************/
+
+DMA_DeviceAttribute_t DMA_gDeviceAttribute[DMA_NUM_DEVICE_ENTRIES] = {
+ [DMA_DEVICE_MEM_TO_MEM] = /* MEM 2 MEM */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0 | DMA_DEVICE_FLAG_ON_DMA1,
+ .name = "mem-to-mem",
+ .config = {
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_MEM,
+ .transferMode = dmacHw_TRANSFER_MODE_PERREQUEST,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_64,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+
+ },
+ },
+ [DMA_DEVICE_VPM_MEM_TO_MEM] = /* VPM */
+ {
+ .flags = DMA_DEVICE_FLAG_IS_DEDICATED | DMA_DEVICE_FLAG_NO_ISR,
+ .name = "vpm",
+ .dedicatedController = 0,
+ .dedicatedChannel = 0,
+ /* reserve DMA0:0 for VPM */
+ },
+ [DMA_DEVICE_NAND_MEM_TO_MEM] = /* NAND */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0 | DMA_DEVICE_FLAG_ON_DMA1,
+ .name = "nand",
+ .config = {
+ .srcPeripheralPort = 0,
+ .dstPeripheralPort = 0,
+ .srcStatusRegisterAddress = 0x00000000,
+ .dstStatusRegisterAddress = 0x00000000,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_MEM,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_32,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_32,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_4,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_4,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_6,
+ },
+ },
+ [DMA_DEVICE_PIF_MEM_TO_DEV] = /* PIF TX */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0 | DMA_DEVICE_FLAG_ON_DMA1
+ | DMA_DEVICE_FLAG_ALLOW_LARGE_FIFO
+ | DMA_DEVICE_FLAG_ALLOC_DMA1_FIRST | DMA_DEVICE_FLAG_PORT_PER_DMAC,
+ .name = "pif_tx",
+ .dmacPort = {14, 5},
+ .config = {
+ .srcPeripheralPort = 0, /* SRC: memory */
+ /* dstPeripheralPort = 5 or 14 */
+ .srcStatusRegisterAddress = 0x00000000,
+ .dstStatusRegisterAddress = 0x00000000,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_2,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_64,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_32,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_8,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_8,
+ .maxDataPerBlock = 16256,
+ },
+ },
+ [DMA_DEVICE_PIF_DEV_TO_MEM] = /* PIF RX */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0 | DMA_DEVICE_FLAG_ON_DMA1
+ | DMA_DEVICE_FLAG_ALLOW_LARGE_FIFO
+ /* DMA_DEVICE_FLAG_ALLOC_DMA1_FIRST */
+ | DMA_DEVICE_FLAG_PORT_PER_DMAC,
+ .name = "pif_rx",
+ .dmacPort = {14, 5},
+ .config = {
+ /* srcPeripheralPort = 5 or 14 */
+ .dstPeripheralPort = 0, /* DST: memory */
+ .srcStatusRegisterAddress = 0x00000000,
+ .dstStatusRegisterAddress = 0x00000000,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .transferType = dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_2,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_32,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_8,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_8,
+ .maxDataPerBlock = 16256,
+ },
+ },
+ [DMA_DEVICE_I2S0_DEV_TO_MEM] = /* I2S RX */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0,
+ .name = "i2s0_rx",
+ .config = {
+ .srcPeripheralPort = 0, /* SRC: I2S0 */
+ .dstPeripheralPort = 0, /* DST: memory */
+ .srcStatusRegisterAddress = 0,
+ .dstStatusRegisterAddress = 0,
+ .transferType = dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_16,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_4,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_0,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .transferMode = dmacHw_TRANSFER_MODE_CONTINUOUS,
+ },
+ },
+ [DMA_DEVICE_I2S0_MEM_TO_DEV] = /* I2S TX */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0,
+ .name = "i2s0_tx",
+ .config = {
+ .srcPeripheralPort = 0, /* SRC: memory */
+ .dstPeripheralPort = 1, /* DST: I2S0 */
+ .srcStatusRegisterAddress = 0,
+ .dstStatusRegisterAddress = 0,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_64,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_16,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_0,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_4,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .transferMode = dmacHw_TRANSFER_MODE_PERREQUEST,
+ },
+ },
+ [DMA_DEVICE_I2S1_DEV_TO_MEM] = /* I2S1 RX */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA1,
+ .name = "i2s1_rx",
+ .config = {
+ .srcPeripheralPort = 2, /* SRC: I2S1 */
+ .dstPeripheralPort = 0, /* DST: memory */
+ .srcStatusRegisterAddress = 0,
+ .dstStatusRegisterAddress = 0,
+ .transferType = dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_16,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_4,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_0,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .transferMode = dmacHw_TRANSFER_MODE_CONTINUOUS,
+ },
+ },
+ [DMA_DEVICE_I2S1_MEM_TO_DEV] = /* I2S1 TX */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA1,
+ .name = "i2s1_tx",
+ .config = {
+ .srcPeripheralPort = 0, /* SRC: memory */
+ .dstPeripheralPort = 3, /* DST: I2S1 */
+ .srcStatusRegisterAddress = 0,
+ .dstStatusRegisterAddress = 0,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_64,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_16,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_0,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_4,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .transferMode = dmacHw_TRANSFER_MODE_PERREQUEST,
+ },
+ },
+ [DMA_DEVICE_ESW_MEM_TO_DEV] = /* ESW TX */
+ {
+ .name = "esw_tx",
+ .flags = DMA_DEVICE_FLAG_IS_DEDICATED,
+ .dedicatedController = 1,
+ .dedicatedChannel = 3,
+ .config = {
+ .srcPeripheralPort = 0, /* SRC: memory */
+ .dstPeripheralPort = 1, /* DST: ESW (MTP) */
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_DISABLE,
+ /* DMAx_AHB_SSTATARy */
+ .srcStatusRegisterAddress = 0x00000000,
+ /* DMAx_AHB_DSTATARy */
+ .dstStatusRegisterAddress = 0x30490010,
+ /* DMAx_AHB_CFGy */
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ /* DMAx_AHB_CTLy */
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_2,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_0,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_8,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_64,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+ },
+ },
+ [DMA_DEVICE_ESW_DEV_TO_MEM] = /* ESW RX */
+ {
+ .name = "esw_rx",
+ .flags = DMA_DEVICE_FLAG_IS_DEDICATED,
+ .dedicatedController = 1,
+ .dedicatedChannel = 2,
+ .config = {
+ .srcPeripheralPort = 0, /* SRC: ESW (PTM) */
+ .dstPeripheralPort = 0, /* DST: memory */
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_DISABLE,
+ /* DMAx_AHB_SSTATARy */
+ .srcStatusRegisterAddress = 0x30480010,
+ /* DMAx_AHB_DSTATARy */
+ .dstStatusRegisterAddress = 0x00000000,
+ /* DMAx_AHB_CFGy */
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ /* DMAx_AHB_CTLy */
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_2,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .transferType = dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_8,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_0,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_64,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+ },
+ },
+ [DMA_DEVICE_APM_CODEC_A_DEV_TO_MEM] = /* APM Codec A Ingress */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0,
+ .name = "apm_a_rx",
+ .config = {
+ .srcPeripheralPort = 2, /* SRC: Codec A Ingress FIFO */
+ .dstPeripheralPort = 0, /* DST: memory */
+ .srcStatusRegisterAddress = 0x00000000,
+ .dstStatusRegisterAddress = 0x00000000,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .transferType = dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_2,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_32,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_4,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_4,
+ .transferMode = dmacHw_TRANSFER_MODE_CONTINUOUS,
+ },
+ },
+ [DMA_DEVICE_APM_CODEC_A_MEM_TO_DEV] = /* APM Codec A Egress */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0,
+ .name = "apm_a_tx",
+ .config = {
+ .srcPeripheralPort = 0, /* SRC: memory */
+ .dstPeripheralPort = 3, /* DST: Codec A Egress FIFO */
+ .srcStatusRegisterAddress = 0x00000000,
+ .dstStatusRegisterAddress = 0x00000000,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_2,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_64,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_32,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_4,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_4,
+ .transferMode = dmacHw_TRANSFER_MODE_PERREQUEST,
+ },
+ },
+ [DMA_DEVICE_APM_CODEC_B_DEV_TO_MEM] = /* APM Codec B Ingress */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0,
+ .name = "apm_b_rx",
+ .config = {
+ .srcPeripheralPort = 4, /* SRC: Codec B Ingress FIFO */
+ .dstPeripheralPort = 0, /* DST: memory */
+ .srcStatusRegisterAddress = 0x00000000,
+ .dstStatusRegisterAddress = 0x00000000,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .transferType = dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_2,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_32,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_4,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_4,
+ .transferMode = dmacHw_TRANSFER_MODE_CONTINUOUS,
+ },
+ },
+ [DMA_DEVICE_APM_CODEC_B_MEM_TO_DEV] = /* APM Codec B Egress */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0,
+ .name = "apm_b_tx",
+ .config = {
+ .srcPeripheralPort = 0, /* SRC: memory */
+ .dstPeripheralPort = 5, /* DST: Codec B Egress FIFO */
+ .srcStatusRegisterAddress = 0x00000000,
+ .dstStatusRegisterAddress = 0x00000000,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_2,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_64,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_32,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_4,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_4,
+ .transferMode = dmacHw_TRANSFER_MODE_PERREQUEST,
+ },
+ },
+ [DMA_DEVICE_APM_CODEC_C_DEV_TO_MEM] = /* APM Codec C Ingress */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA1,
+ .name = "apm_c_rx",
+ .config = {
+ .srcPeripheralPort = 4, /* SRC: Codec C Ingress FIFO */
+ .dstPeripheralPort = 0, /* DST: memory */
+ .srcStatusRegisterAddress = 0x00000000,
+ .dstStatusRegisterAddress = 0x00000000,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .transferType = dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_2,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_32,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_4,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_4,
+ .transferMode = dmacHw_TRANSFER_MODE_CONTINUOUS,
+ },
+ },
+ [DMA_DEVICE_APM_PCM0_DEV_TO_MEM] = /* PCM0 RX */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0,
+ .name = "pcm0_rx",
+ .config = {
+ .srcPeripheralPort = 12, /* SRC: PCM0 */
+ .dstPeripheralPort = 0, /* DST: memory */
+ .srcStatusRegisterAddress = 0,
+ .dstStatusRegisterAddress = 0,
+ .transferType = dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_2,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_32,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_8,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_4,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .transferMode = dmacHw_TRANSFER_MODE_CONTINUOUS,
+ },
+ },
+ [DMA_DEVICE_APM_PCM0_MEM_TO_DEV] = /* PCM0 TX */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0,
+ .name = "pcm0_tx",
+ .config = {
+ .srcPeripheralPort = 0, /* SRC: memory */
+ .dstPeripheralPort = 13, /* DST: PCM0 */
+ .srcStatusRegisterAddress = 0,
+ .dstStatusRegisterAddress = 0,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_2,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_64,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_32,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_4,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_8,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .transferMode = dmacHw_TRANSFER_MODE_PERREQUEST,
+ },
+ },
+ [DMA_DEVICE_APM_PCM1_DEV_TO_MEM] = /* PCM1 RX */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA1,
+ .name = "pcm1_rx",
+ .config = {
+ .srcPeripheralPort = 14, /* SRC: PCM1 */
+ .dstPeripheralPort = 0, /* DST: memory */
+ .srcStatusRegisterAddress = 0,
+ .dstStatusRegisterAddress = 0,
+ .transferType = dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_2,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_32,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_8,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_4,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .transferMode = dmacHw_TRANSFER_MODE_CONTINUOUS,
+ },
+ },
+ [DMA_DEVICE_APM_PCM1_MEM_TO_DEV] = /* PCM1 TX */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA1,
+ .name = "pcm1_tx",
+ .config = {
+ .srcPeripheralPort = 0, /* SRC: memory */
+ .dstPeripheralPort = 15, /* DST: PCM1 */
+ .srcStatusRegisterAddress = 0,
+ .dstStatusRegisterAddress = 0,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_2,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_64,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_32,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_4,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_8,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .transferMode = dmacHw_TRANSFER_MODE_PERREQUEST,
+ },
+ },
+ [DMA_DEVICE_SPUM_DEV_TO_MEM] = /* SPUM RX */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0 | DMA_DEVICE_FLAG_ON_DMA1,
+ .name = "spum_rx",
+ .config = {
+ .srcPeripheralPort = 6, /* SRC: Codec A Ingress FIFO */
+ .dstPeripheralPort = 0, /* DST: memory */
+ .srcStatusRegisterAddress = 0x00000000,
+ .dstStatusRegisterAddress = 0x00000000,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .transferType = dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_2,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_32,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_32,
+ /* Busrt size **MUST** be 16 for SPUM to work */
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_16,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_16,
+ .transferMode = dmacHw_TRANSFER_MODE_PERREQUEST,
+ /* on the RX side, SPU needs to be the flow controller */
+ .flowControler = dmacHw_FLOW_CONTROL_PERIPHERAL,
+ },
+ },
+ [DMA_DEVICE_SPUM_MEM_TO_DEV] = /* SPUM TX */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0 | DMA_DEVICE_FLAG_ON_DMA1,
+ .name = "spum_tx",
+ .config = {
+ .srcPeripheralPort = 0, /* SRC: memory */
+ .dstPeripheralPort = 7, /* DST: SPUM */
+ .srcStatusRegisterAddress = 0x00000000,
+ .dstStatusRegisterAddress = 0x00000000,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_2,
+ .blockTransferInterrupt = dmacHw_INTERRUPT_DISABLE,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_32,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_32,
+ /* Busrt size **MUST** be 16 for SPUM to work */
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_16,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_16,
+ .transferMode = dmacHw_TRANSFER_MODE_PERREQUEST,
+ },
+ },
+ [DMA_DEVICE_MEM_TO_VRAM] = /* MEM 2 VRAM */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0 | DMA_DEVICE_FLAG_ON_DMA1,
+ .name = "mem-to-vram",
+ .config = {
+ .srcPeripheralPort = 0, /* SRC: memory */
+ .srcStatusRegisterAddress = 0x00000000,
+ .dstStatusRegisterAddress = 0x00000000,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_MEM,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_1,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_2,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_64,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_8,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_8,
+ },
+ },
+ [DMA_DEVICE_VRAM_TO_MEM] = /* VRAM 2 MEM */
+ {
+ .flags = DMA_DEVICE_FLAG_ON_DMA0 | DMA_DEVICE_FLAG_ON_DMA1,
+ .name = "vram-to-mem",
+ .config = {
+ .dstPeripheralPort = 0, /* DST: memory */
+ .srcStatusRegisterAddress = 0x00000000,
+ .dstStatusRegisterAddress = 0x00000000,
+ .srcUpdate = dmacHw_SRC_ADDRESS_UPDATE_MODE_INC,
+ .dstUpdate = dmacHw_DST_ADDRESS_UPDATE_MODE_INC,
+ .transferType = dmacHw_TRANSFER_TYPE_MEM_TO_MEM,
+ .srcMasterInterface = dmacHw_SRC_MASTER_INTERFACE_2,
+ .dstMasterInterface = dmacHw_DST_MASTER_INTERFACE_1,
+ .completeTransferInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .errorInterrupt = dmacHw_INTERRUPT_ENABLE,
+ .channelPriority = dmacHw_CHANNEL_PRIORITY_7,
+ .srcMaxTransactionWidth = dmacHw_SRC_TRANSACTION_WIDTH_64,
+ .dstMaxTransactionWidth = dmacHw_DST_TRANSACTION_WIDTH_64,
+ .srcMaxBurstWidth = dmacHw_SRC_BURST_WIDTH_8,
+ .dstMaxBurstWidth = dmacHw_DST_BURST_WIDTH_8,
+ },
+ },
+};
+EXPORT_SYMBOL(DMA_gDeviceAttribute); /* primarily for dma-test.c */
diff --git a/arch/arm/mach-bcmring/include/cfg_global.h b/arch/arm/mach-bcmring/include/cfg_global.h
new file mode 100644
index 00000000000..f01da877148
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/cfg_global.h
@@ -0,0 +1,13 @@
+#ifndef _CFG_GLOBAL_H_
+#define _CFG_GLOBAL_H_
+
+#include <cfg_global_defines.h>
+
+#define CFG_GLOBAL_CHIP BCM11107
+#define CFG_GLOBAL_CHIP_FAMILY CFG_GLOBAL_CHIP_FAMILY_BCMRING
+#define CFG_GLOBAL_CHIP_REV 0xB0
+#define CFG_GLOBAL_RAM_SIZE 0x10000000
+#define CFG_GLOBAL_RAM_BASE 0x00000000
+#define CFG_GLOBAL_RAM_RESERVED_SIZE 0x000000
+
+#endif /* _CFG_GLOBAL_H_ */
diff --git a/arch/arm/mach-bcmring/include/cfg_global_defines.h b/arch/arm/mach-bcmring/include/cfg_global_defines.h
new file mode 100644
index 00000000000..b5beb0b3073
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/cfg_global_defines.h
@@ -0,0 +1,40 @@
+/*****************************************************************************
+* Copyright 2006 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#ifndef CFG_GLOBAL_DEFINES_H
+#define CFG_GLOBAL_DEFINES_H
+
+/* CHIP */
+#define BCM1103 1
+
+#define BCM1191 4
+#define BCM2153 5
+#define BCM2820 6
+
+#define BCM2826 8
+#define FPGA11107 9
+#define BCM11107 10
+#define BCM11109 11
+#define BCM11170 12
+#define BCM11110 13
+#define BCM11211 14
+
+/* CFG_GLOBAL_CHIP_FAMILY types */
+#define CFG_GLOBAL_CHIP_FAMILY_NONE 0
+#define CFG_GLOBAL_CHIP_FAMILY_BCM116X 2
+#define CFG_GLOBAL_CHIP_FAMILY_BCMRING 4
+#define CFG_GLOBAL_CHIP_FAMILY_BCM1103 8
+
+#define IMAGE_HEADER_SIZE_CHECKSUM 4
+#endif
diff --git a/arch/arm/mach-bcmring/include/csp/cache.h b/arch/arm/mach-bcmring/include/csp/cache.h
new file mode 100644
index 00000000000..caa20e59db9
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/csp/cache.h
@@ -0,0 +1,35 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#ifndef CSP_CACHE_H
+#define CSP_CACHE_H
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <csp/stdint.h>
+
+/* ---- Public Constants and Types --------------------------------------- */
+
+#if defined(__KERNEL__) && !defined(STANDALONE)
+#include <asm/cacheflush.h>
+
+#define CSP_CACHE_FLUSH_ALL flush_cache_all()
+
+#else
+
+#define CSP_CACHE_FLUSH_ALL
+
+#endif
+
+#endif /* CSP_CACHE_H */
diff --git a/arch/arm/mach-bcmring/include/csp/delay.h b/arch/arm/mach-bcmring/include/csp/delay.h
new file mode 100644
index 00000000000..8b3d8036729
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/csp/delay.h
@@ -0,0 +1,36 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+
+#ifndef CSP_DELAY_H
+#define CSP_DELAY_H
+
+/* ---- Include Files ---------------------------------------------------- */
+
+/* Some CSP routines require use of the following delay routines. Use the OS */
+/* version if available, otherwise use a CSP specific definition. */
+/* void udelay(unsigned long usecs); */
+/* void mdelay(unsigned long msecs); */
+
+#if defined(__KERNEL__) && !defined(STANDALONE)
+ #include <linux/delay.h>
+#else
+ #include <mach/csp/delay.h>
+#endif
+
+/* ---- Public Constants and Types --------------------------------------- */
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+
+#endif /* CSP_DELAY_H */
diff --git a/arch/arm/mach-bcmring/include/csp/dmacHw.h b/arch/arm/mach-bcmring/include/csp/dmacHw.h
new file mode 100644
index 00000000000..5d510130a25
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/csp/dmacHw.h
@@ -0,0 +1,596 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file dmacHw.h
+*
+* @brief API definitions for low level DMA controller driver
+*
+*/
+/****************************************************************************/
+#ifndef _DMACHW_H
+#define _DMACHW_H
+
+#include <stddef.h>
+
+#include <csp/stdint.h>
+#include <mach/csp/dmacHw_reg.h>
+
+/* Define DMA Channel ID using DMA controller number (m) and channel number (c).
+
+ System specific channel ID should be defined as follows
+
+ For example:
+
+ #include <dmacHw.h>
+ ...
+ #define systemHw_LCD_CHANNEL_ID dmacHw_MAKE_CHANNEL_ID(0,5)
+ #define systemHw_SWITCH_RX_CHANNEL_ID dmacHw_MAKE_CHANNEL_ID(0,0)
+ #define systemHw_SWITCH_TX_CHANNEL_ID dmacHw_MAKE_CHANNEL_ID(0,1)
+ #define systemHw_APM_RX_CHANNEL_ID dmacHw_MAKE_CHANNEL_ID(0,3)
+ #define systemHw_APM_TX_CHANNEL_ID dmacHw_MAKE_CHANNEL_ID(0,4)
+ ...
+ #define systemHw_SHARED1_CHANNEL_ID dmacHw_MAKE_CHANNEL_ID(1,4)
+ #define systemHw_SHARED2_CHANNEL_ID dmacHw_MAKE_CHANNEL_ID(1,5)
+ #define systemHw_SHARED3_CHANNEL_ID dmacHw_MAKE_CHANNEL_ID(0,6)
+ ...
+*/
+#define dmacHw_MAKE_CHANNEL_ID(m, c) (m << 8 | c)
+
+typedef enum {
+ dmacHw_CHANNEL_PRIORITY_0 = dmacHw_REG_CFG_LO_CH_PRIORITY_0, /* Channel priority 0. Lowest priority DMA channel */
+ dmacHw_CHANNEL_PRIORITY_1 = dmacHw_REG_CFG_LO_CH_PRIORITY_1, /* Channel priority 1 */
+ dmacHw_CHANNEL_PRIORITY_2 = dmacHw_REG_CFG_LO_CH_PRIORITY_2, /* Channel priority 2 */
+ dmacHw_CHANNEL_PRIORITY_3 = dmacHw_REG_CFG_LO_CH_PRIORITY_3, /* Channel priority 3 */
+ dmacHw_CHANNEL_PRIORITY_4 = dmacHw_REG_CFG_LO_CH_PRIORITY_4, /* Channel priority 4 */
+ dmacHw_CHANNEL_PRIORITY_5 = dmacHw_REG_CFG_LO_CH_PRIORITY_5, /* Channel priority 5 */
+ dmacHw_CHANNEL_PRIORITY_6 = dmacHw_REG_CFG_LO_CH_PRIORITY_6, /* Channel priority 6 */
+ dmacHw_CHANNEL_PRIORITY_7 = dmacHw_REG_CFG_LO_CH_PRIORITY_7 /* Channel priority 7. Highest priority DMA channel */
+} dmacHw_CHANNEL_PRIORITY_e;
+
+/* Source destination master interface */
+typedef enum {
+ dmacHw_SRC_MASTER_INTERFACE_1 = dmacHw_REG_CTL_SMS_1, /* Source DMA master interface 1 */
+ dmacHw_SRC_MASTER_INTERFACE_2 = dmacHw_REG_CTL_SMS_2, /* Source DMA master interface 2 */
+ dmacHw_DST_MASTER_INTERFACE_1 = dmacHw_REG_CTL_DMS_1, /* Destination DMA master interface 1 */
+ dmacHw_DST_MASTER_INTERFACE_2 = dmacHw_REG_CTL_DMS_2 /* Destination DMA master interface 2 */
+} dmacHw_MASTER_INTERFACE_e;
+
+typedef enum {
+ dmacHw_SRC_TRANSACTION_WIDTH_8 = dmacHw_REG_CTL_SRC_TR_WIDTH_8, /* Source 8 bit (1 byte) per transaction */
+ dmacHw_SRC_TRANSACTION_WIDTH_16 = dmacHw_REG_CTL_SRC_TR_WIDTH_16, /* Source 16 bit (2 byte) per transaction */
+ dmacHw_SRC_TRANSACTION_WIDTH_32 = dmacHw_REG_CTL_SRC_TR_WIDTH_32, /* Source 32 bit (4 byte) per transaction */
+ dmacHw_SRC_TRANSACTION_WIDTH_64 = dmacHw_REG_CTL_SRC_TR_WIDTH_64, /* Source 64 bit (8 byte) per transaction */
+ dmacHw_DST_TRANSACTION_WIDTH_8 = dmacHw_REG_CTL_DST_TR_WIDTH_8, /* Destination 8 bit (1 byte) per transaction */
+ dmacHw_DST_TRANSACTION_WIDTH_16 = dmacHw_REG_CTL_DST_TR_WIDTH_16, /* Destination 16 bit (2 byte) per transaction */
+ dmacHw_DST_TRANSACTION_WIDTH_32 = dmacHw_REG_CTL_DST_TR_WIDTH_32, /* Destination 32 bit (4 byte) per transaction */
+ dmacHw_DST_TRANSACTION_WIDTH_64 = dmacHw_REG_CTL_DST_TR_WIDTH_64 /* Destination 64 bit (8 byte) per transaction */
+} dmacHw_TRANSACTION_WIDTH_e;
+
+typedef enum {
+ dmacHw_SRC_BURST_WIDTH_0 = dmacHw_REG_CTL_SRC_MSIZE_0, /* Source No burst */
+ dmacHw_SRC_BURST_WIDTH_4 = dmacHw_REG_CTL_SRC_MSIZE_4, /* Source 4 X dmacHw_TRANSACTION_WIDTH_xxx bytes per burst */
+ dmacHw_SRC_BURST_WIDTH_8 = dmacHw_REG_CTL_SRC_MSIZE_8, /* Source 8 X dmacHw_TRANSACTION_WIDTH_xxx bytes per burst */
+ dmacHw_SRC_BURST_WIDTH_16 = dmacHw_REG_CTL_SRC_MSIZE_16, /* Source 16 X dmacHw_TRANSACTION_WIDTH_xxx bytes per burst */
+ dmacHw_DST_BURST_WIDTH_0 = dmacHw_REG_CTL_DST_MSIZE_0, /* Destination No burst */
+ dmacHw_DST_BURST_WIDTH_4 = dmacHw_REG_CTL_DST_MSIZE_4, /* Destination 4 X dmacHw_TRANSACTION_WIDTH_xxx bytes per burst */
+ dmacHw_DST_BURST_WIDTH_8 = dmacHw_REG_CTL_DST_MSIZE_8, /* Destination 8 X dmacHw_TRANSACTION_WIDTH_xxx bytes per burst */
+ dmacHw_DST_BURST_WIDTH_16 = dmacHw_REG_CTL_DST_MSIZE_16 /* Destination 16 X dmacHw_TRANSACTION_WIDTH_xxx bytes per burst */
+} dmacHw_BURST_WIDTH_e;
+
+typedef enum {
+ dmacHw_TRANSFER_TYPE_MEM_TO_MEM = dmacHw_REG_CTL_TTFC_MM_DMAC, /* Memory to memory transfer */
+ dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM = dmacHw_REG_CTL_TTFC_PM_DMAC, /* Peripheral to memory transfer */
+ dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL = dmacHw_REG_CTL_TTFC_MP_DMAC, /* Memory to peripheral transfer */
+ dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_PERIPHERAL = dmacHw_REG_CTL_TTFC_PP_DMAC /* Peripheral to peripheral transfer */
+} dmacHw_TRANSFER_TYPE_e;
+
+typedef enum {
+ dmacHw_TRANSFER_MODE_PERREQUEST, /* Block transfer per DMA request */
+ dmacHw_TRANSFER_MODE_CONTINUOUS, /* Continuous transfer of streaming data */
+ dmacHw_TRANSFER_MODE_PERIODIC /* Periodic transfer of streaming data */
+} dmacHw_TRANSFER_MODE_e;
+
+typedef enum {
+ dmacHw_SRC_ADDRESS_UPDATE_MODE_INC = dmacHw_REG_CTL_SINC_INC, /* Increment source address after every transaction */
+ dmacHw_SRC_ADDRESS_UPDATE_MODE_DEC = dmacHw_REG_CTL_SINC_DEC, /* Decrement source address after every transaction */
+ dmacHw_DST_ADDRESS_UPDATE_MODE_INC = dmacHw_REG_CTL_DINC_INC, /* Increment destination address after every transaction */
+ dmacHw_DST_ADDRESS_UPDATE_MODE_DEC = dmacHw_REG_CTL_DINC_DEC, /* Decrement destination address after every transaction */
+ dmacHw_SRC_ADDRESS_UPDATE_MODE_NC = dmacHw_REG_CTL_SINC_NC, /* No change in source address after every transaction */
+ dmacHw_DST_ADDRESS_UPDATE_MODE_NC = dmacHw_REG_CTL_DINC_NC /* No change in destination address after every transaction */
+} dmacHw_ADDRESS_UPDATE_MODE_e;
+
+typedef enum {
+ dmacHw_FLOW_CONTROL_DMA, /* DMA working as flow controller (default) */
+ dmacHw_FLOW_CONTROL_PERIPHERAL /* Peripheral working as flow controller */
+} dmacHw_FLOW_CONTROL_e;
+
+typedef enum {
+ dmacHw_TRANSFER_STATUS_BUSY, /* DMA Transfer ongoing */
+ dmacHw_TRANSFER_STATUS_DONE, /* DMA Transfer completed */
+ dmacHw_TRANSFER_STATUS_ERROR /* DMA Transfer error */
+} dmacHw_TRANSFER_STATUS_e;
+
+typedef enum {
+ dmacHw_INTERRUPT_DISABLE, /* Interrupt disable */
+ dmacHw_INTERRUPT_ENABLE /* Interrupt enable */
+} dmacHw_INTERRUPT_e;
+
+typedef enum {
+ dmacHw_INTERRUPT_STATUS_NONE = 0x0, /* No DMA interrupt */
+ dmacHw_INTERRUPT_STATUS_TRANS = 0x1, /* End of DMA transfer interrupt */
+ dmacHw_INTERRUPT_STATUS_BLOCK = 0x2, /* End of block transfer interrupt */
+ dmacHw_INTERRUPT_STATUS_ERROR = 0x4 /* Error interrupt */
+} dmacHw_INTERRUPT_STATUS_e;
+
+typedef enum {
+ dmacHw_CONTROLLER_ATTRIB_CHANNEL_NUM, /* Number of DMA channel */
+ dmacHw_CONTROLLER_ATTRIB_CHANNEL_MAX_BLOCK_SIZE, /* Maximum channel burst size */
+ dmacHw_CONTROLLER_ATTRIB_MASTER_INTF_NUM, /* Number of DMA master interface */
+ dmacHw_CONTROLLER_ATTRIB_CHANNEL_BUS_WIDTH, /* Channel Data bus width */
+ dmacHw_CONTROLLER_ATTRIB_CHANNEL_FIFO_SIZE /* Channel FIFO size */
+} dmacHw_CONTROLLER_ATTRIB_e;
+
+typedef unsigned long dmacHw_HANDLE_t; /* DMA channel handle */
+typedef uint32_t dmacHw_ID_t; /* DMA channel Id. Must be created using
+ "dmacHw_MAKE_CHANNEL_ID" macro
+ */
+/* DMA channel configuration parameters */
+typedef struct {
+ uint32_t srcPeripheralPort; /* Source peripheral port */
+ uint32_t dstPeripheralPort; /* Destination peripheral port */
+ uint32_t srcStatusRegisterAddress; /* Source status register address */
+ uint32_t dstStatusRegisterAddress; /* Destination status register address of type */
+
+ uint32_t srcGatherWidth; /* Number of bytes gathered before successive gather opearation */
+ uint32_t srcGatherJump; /* Number of bytes jumpped before successive gather opearation */
+ uint32_t dstScatterWidth; /* Number of bytes sacattered before successive scatter opearation */
+ uint32_t dstScatterJump; /* Number of bytes jumpped before successive scatter opearation */
+ uint32_t maxDataPerBlock; /* Maximum number of bytes to be transferred per block/descrptor.
+ 0 = Maximum possible.
+ */
+
+ dmacHw_ADDRESS_UPDATE_MODE_e srcUpdate; /* Source address update mode */
+ dmacHw_ADDRESS_UPDATE_MODE_e dstUpdate; /* Destination address update mode */
+ dmacHw_TRANSFER_TYPE_e transferType; /* DMA transfer type */
+ dmacHw_TRANSFER_MODE_e transferMode; /* DMA transfer mode */
+ dmacHw_MASTER_INTERFACE_e srcMasterInterface; /* DMA source interface */
+ dmacHw_MASTER_INTERFACE_e dstMasterInterface; /* DMA destination interface */
+ dmacHw_TRANSACTION_WIDTH_e srcMaxTransactionWidth; /* Source transaction width */
+ dmacHw_TRANSACTION_WIDTH_e dstMaxTransactionWidth; /* Destination transaction width */
+ dmacHw_BURST_WIDTH_e srcMaxBurstWidth; /* Source burst width */
+ dmacHw_BURST_WIDTH_e dstMaxBurstWidth; /* Destination burst width */
+ dmacHw_INTERRUPT_e blockTransferInterrupt; /* Block trsnafer interrupt */
+ dmacHw_INTERRUPT_e completeTransferInterrupt; /* Complete DMA trsnafer interrupt */
+ dmacHw_INTERRUPT_e errorInterrupt; /* Error interrupt */
+ dmacHw_CHANNEL_PRIORITY_e channelPriority; /* Channel priority */
+ dmacHw_FLOW_CONTROL_e flowControler; /* Data flow controller */
+} dmacHw_CONFIG_t;
+
+/****************************************************************************/
+/**
+* @brief Initializes DMA
+*
+* This function initializes DMA CSP driver
+*
+* @note
+* Must be called before using any DMA channel
+*/
+/****************************************************************************/
+void dmacHw_initDma(void);
+
+/****************************************************************************/
+/**
+* @brief Exit function for DMA
+*
+* This function isolates DMA from the system
+*
+*/
+/****************************************************************************/
+void dmacHw_exitDma(void);
+
+/****************************************************************************/
+/**
+* @brief Gets a handle to a DMA channel
+*
+* This function returns a handle, representing a control block of a particular DMA channel
+*
+* @return -1 - On Failure
+* handle - On Success, representing a channel control block
+*
+* @note
+* None Channel ID must be created using "dmacHw_MAKE_CHANNEL_ID" macro
+*/
+/****************************************************************************/
+dmacHw_HANDLE_t dmacHw_getChannelHandle(dmacHw_ID_t channelId /* [ IN ] DMA Channel Id */
+ );
+
+/****************************************************************************/
+/**
+* @brief Initializes a DMA channel for use
+*
+* This function initializes and resets a DMA channel for use
+*
+* @return -1 - On Failure
+* 0 - On Success
+*
+* @note
+* None
+*/
+/****************************************************************************/
+int dmacHw_initChannel(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ );
+
+/****************************************************************************/
+/**
+* @brief Estimates number of descriptor needed to perform certain DMA transfer
+*
+*
+* @return On failure : -1
+* On success : Number of descriptor count
+*
+*
+*/
+/****************************************************************************/
+int dmacHw_calculateDescriptorCount(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pSrcAddr, /* [ IN ] Source (Peripheral/Memory) address */
+ void *pDstAddr, /* [ IN ] Destination (Peripheral/Memory) address */
+ size_t dataLen /* [ IN ] Data length in bytes */
+ );
+
+/****************************************************************************/
+/**
+* @brief Initializes descriptor ring
+*
+* This function will initializes the descriptor ring of a DMA channel
+*
+*
+* @return -1 - On failure
+* 0 - On success
+* @note
+* - "len" parameter should be obtained from "dmacHw_descriptorLen"
+* - Descriptor buffer MUST be 32 bit aligned and uncached as it
+* is accessed by ARM and DMA
+*/
+/****************************************************************************/
+int dmacHw_initDescriptor(void *pDescriptorVirt, /* [ IN ] Virtual address of uncahced buffer allocated to form descriptor ring */
+ uint32_t descriptorPhyAddr, /* [ IN ] Physical address of pDescriptorVirt (descriptor buffer) */
+ uint32_t len, /* [ IN ] Size of the pBuf */
+ uint32_t num /* [ IN ] Number of descriptor in the ring */
+ );
+
+/****************************************************************************/
+/**
+* @brief Finds amount of memory required to form a descriptor ring
+*
+*
+* @return Number of bytes required to form a descriptor ring
+*
+*
+* @note
+* None
+*/
+/****************************************************************************/
+uint32_t dmacHw_descriptorLen(uint32_t descCnt /* [ IN ] Number of descriptor in the ring */
+ );
+
+/****************************************************************************/
+/**
+* @brief Configure DMA channel
+*
+* @return 0 : On success
+* -1 : On failure
+*/
+/****************************************************************************/
+int dmacHw_configChannel(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ dmacHw_CONFIG_t *pConfig /* [ IN ] Configuration settings */
+ );
+
+/****************************************************************************/
+/**
+* @brief Set descriptors for known data length
+*
+* When DMA has to work as a flow controller, this function prepares the
+* descriptor chain to transfer data
+*
+* from:
+* - Memory to memory
+* - Peripheral to memory
+* - Memory to Peripheral
+* - Peripheral to Peripheral
+*
+* @return -1 - On failure
+* 0 - On success
+*
+*/
+/****************************************************************************/
+int dmacHw_setDataDescriptor(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ void *pSrcAddr, /* [ IN ] Source (Peripheral/Memory) address */
+ void *pDstAddr, /* [ IN ] Destination (Peripheral/Memory) address */
+ size_t dataLen /* [ IN ] Length in bytes */
+ );
+
+/****************************************************************************/
+/**
+* @brief Indicates whether DMA transfer is in progress or completed
+*
+* @return DMA transfer status
+* dmacHw_TRANSFER_STATUS_BUSY: DMA Transfer ongoing
+* dmacHw_TRANSFER_STATUS_DONE: DMA Transfer completed
+* dmacHw_TRANSFER_STATUS_ERROR: DMA Transfer error
+*
+*/
+/****************************************************************************/
+dmacHw_TRANSFER_STATUS_e dmacHw_transferCompleted(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ );
+
+/****************************************************************************/
+/**
+* @brief Set descriptor carrying control information
+*
+* This function will be used to send specific control information to the device
+* using the DMA channel
+*
+*
+* @return -1 - On failure
+* 0 - On success
+*
+* @note
+* None
+*/
+/****************************************************************************/
+int dmacHw_setControlDescriptor(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ uint32_t ctlAddress, /* [ IN ] Address of the device control register */
+ uint32_t control /* [ IN ] Device control information */
+ );
+
+/****************************************************************************/
+/**
+* @brief Read data DMA transferred to memory
+*
+* This function will read data that has been DMAed to memory while transfering from:
+* - Memory to memory
+* - Peripheral to memory
+*
+* @return 0 - No more data is available to read
+* 1 - More data might be available to read
+*
+*/
+/****************************************************************************/
+int dmacHw_readTransferredData(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ void **ppBbuf, /* [ OUT ] Data received */
+ size_t *pLlen /* [ OUT ] Length of the data received */
+ );
+
+/****************************************************************************/
+/**
+* @brief Prepares descriptor ring, when source peripheral working as a flow controller
+*
+* This function will form the descriptor ring by allocating buffers, when source peripheral
+* has to work as a flow controller to transfer data from:
+* - Peripheral to memory.
+*
+* @return -1 - On failure
+* 0 - On success
+*
+*
+* @note
+* None
+*/
+/****************************************************************************/
+int dmacHw_setVariableDataDescriptor(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ uint32_t srcAddr, /* [ IN ] Source peripheral address */
+ void *(*fpAlloc) (int len), /* [ IN ] Function pointer that provides destination memory */
+ int len, /* [ IN ] Number of bytes "fpAlloc" will allocate for destination */
+ int num /* [ IN ] Number of descriptor to set */
+ );
+
+/****************************************************************************/
+/**
+* @brief Program channel register to initiate transfer
+*
+* @return void
+*
+*
+* @note
+* - Descriptor buffer MUST ALWAYS be flushed before calling this function
+* - This function should also be called from ISR to program the channel with
+* pending descriptors
+*/
+/****************************************************************************/
+void dmacHw_initiateTransfer(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor /* [ IN ] Descriptor buffer */
+ );
+
+/****************************************************************************/
+/**
+* @brief Resets descriptor control information
+*
+* @return void
+*/
+/****************************************************************************/
+void dmacHw_resetDescriptorControl(void *pDescriptor /* [ IN ] Descriptor buffer */
+ );
+
+/****************************************************************************/
+/**
+* @brief Program channel register to stop transfer
+*
+* Ensures the channel is not doing any transfer after calling this function
+*
+* @return void
+*
+*/
+/****************************************************************************/
+void dmacHw_stopTransfer(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ );
+
+/****************************************************************************/
+/**
+* @brief Check the existance of pending descriptor
+*
+* This function confirmes if there is any pending descriptor in the chain
+* to program the channel
+*
+* @return 1 : Channel need to be programmed with pending descriptor
+* 0 : No more pending descriptor to programe the channel
+*
+* @note
+* - This function should be called from ISR in case there are pending
+* descriptor to program the channel.
+*
+* Example:
+*
+* dmac_isr ()
+* {
+* ...
+* if (dmacHw_descriptorPending (handle))
+* {
+* dmacHw_initiateTransfer (handle);
+* }
+* }
+*
+*/
+/****************************************************************************/
+uint32_t dmacHw_descriptorPending(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ void *pDescriptor /* [ IN ] Descriptor buffer */
+ );
+
+/****************************************************************************/
+/**
+* @brief Deallocates source or destination memory, allocated
+*
+* This function can be called to deallocate data memory that was DMAed successfully
+*
+* @return -1 - On failure
+* 0 - On success
+*
+* @note
+* This function will be called ONLY, when source OR destination address is pointing
+* to dynamic memory
+*/
+/****************************************************************************/
+int dmacHw_freeMem(dmacHw_CONFIG_t *pConfig, /* [ IN ] Configuration settings */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ void (*fpFree) (void *) /* [ IN ] Function pointer to free data memory */
+ );
+
+/****************************************************************************/
+/**
+* @brief Clears the interrupt
+*
+* This function clears the DMA channel specific interrupt
+*
+* @return N/A
+*
+* @note
+* Must be called under the context of ISR
+*/
+/****************************************************************************/
+void dmacHw_clearInterrupt(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ );
+
+/****************************************************************************/
+/**
+* @brief Returns the cause of channel specific DMA interrupt
+*
+* This function returns the cause of interrupt
+*
+* @return Interrupt status, each bit representing a specific type of interrupt
+* of type dmacHw_INTERRUPT_STATUS_e
+* @note
+* This function should be called under the context of ISR
+*/
+/****************************************************************************/
+dmacHw_INTERRUPT_STATUS_e dmacHw_getInterruptStatus(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ );
+
+/****************************************************************************/
+/**
+* @brief Indentifies a DMA channel causing interrupt
+*
+* This functions returns a channel causing interrupt of type dmacHw_INTERRUPT_STATUS_e
+*
+* @return NULL : No channel causing DMA interrupt
+* ! NULL : Handle to a channel causing DMA interrupt
+* @note
+* dmacHw_clearInterrupt() must be called with a valid handle after calling this function
+*/
+/****************************************************************************/
+dmacHw_HANDLE_t dmacHw_getInterruptSource(void);
+
+/****************************************************************************/
+/**
+* @brief Sets channel specific user data
+*
+* This function associates user data to a specif DMA channel
+*
+*/
+/****************************************************************************/
+void dmacHw_setChannelUserData(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ void *userData /* [ IN ] User data */
+ );
+
+/****************************************************************************/
+/**
+* @brief Gets channel specific user data
+*
+* This function returns user data specific to a DMA channel
+*
+* @return user data
+*/
+/****************************************************************************/
+void *dmacHw_getChannelUserData(dmacHw_HANDLE_t handle /* [ IN ] DMA Channel handle */
+ );
+
+/****************************************************************************/
+/**
+* @brief Displays channel specific registers and other control parameters
+*
+*
+* @return void
+*
+* @note
+* None
+*/
+/****************************************************************************/
+void dmacHw_printDebugInfo(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ void *pDescriptor, /* [ IN ] Descriptor buffer */
+ int (*fpPrint) (const char *, ...) /* [ IN ] Print callback function */
+ );
+
+/****************************************************************************/
+/**
+* @brief Provides DMA controller attributes
+*
+*
+* @return DMA controller attributes
+*
+* @note
+* None
+*/
+/****************************************************************************/
+uint32_t dmacHw_getDmaControllerAttribute(dmacHw_HANDLE_t handle, /* [ IN ] DMA Channel handle */
+ dmacHw_CONTROLLER_ATTRIB_e attr /* [ IN ] DMA Controler attribute of type dmacHw_CONTROLLER_ATTRIB_e */
+ );
+
+#endif /* _DMACHW_H */
diff --git a/arch/arm/mach-bcmring/include/csp/errno.h b/arch/arm/mach-bcmring/include/csp/errno.h
new file mode 100644
index 00000000000..51357dd5b66
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/csp/errno.h
@@ -0,0 +1,32 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#ifndef CSP_ERRNO_H
+#define CSP_ERRNO_H
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#if defined(__KERNEL__)
+#include <linux/errno.h>
+#elif defined(CSP_SIMULATION)
+#include <asm-generic/errno.h>
+#else
+#include <errno.h>
+#endif
+
+/* ---- Public Constants and Types --------------------------------------- */
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+
+#endif /* CSP_ERRNO_H */
diff --git a/arch/arm/mach-bcmring/include/csp/intcHw.h b/arch/arm/mach-bcmring/include/csp/intcHw.h
new file mode 100644
index 00000000000..1c639c8ee08
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/csp/intcHw.h
@@ -0,0 +1,40 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+
+/****************************************************************************/
+/**
+* @file intcHw.h
+*
+* @brief generic interrupt controller API
+*
+* @note
+* None
+*/
+/****************************************************************************/
+
+#ifndef _INTCHW_H
+#define _INTCHW_H
+
+/* ---- Include Files ---------------------------------------------------- */
+#include <mach/csp/intcHw_reg.h>
+
+/* ---- Public Constants and Types --------------------------------------- */
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+static inline void intcHw_irq_disable(void *basep, uint32_t mask);
+static inline void intcHw_irq_enable(void *basep, uint32_t mask);
+
+#endif /* _INTCHW_H */
+
diff --git a/arch/arm/mach-bcmring/include/csp/module.h b/arch/arm/mach-bcmring/include/csp/module.h
new file mode 100644
index 00000000000..c30d2a5975a
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/csp/module.h
@@ -0,0 +1,32 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+
+#ifndef CSP_MODULE_H
+#define CSP_MODULE_H
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#ifdef __KERNEL__
+ #include <linux/module.h>
+#else
+ #define EXPORT_SYMBOL(symbol)
+#endif
+
+/* ---- Public Constants and Types --------------------------------------- */
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+
+
+#endif /* CSP_MODULE_H */
diff --git a/arch/arm/mach-bcmring/include/csp/reg.h b/arch/arm/mach-bcmring/include/csp/reg.h
new file mode 100644
index 00000000000..e5f60bf5a1f
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/csp/reg.h
@@ -0,0 +1,114 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file reg.h
+*
+* @brief Generic register defintions used in CSP
+*/
+/****************************************************************************/
+
+#ifndef CSP_REG_H
+#define CSP_REG_H
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <csp/stdint.h>
+
+/* ---- Public Constants and Types --------------------------------------- */
+
+#define __REG32(x) (*((volatile uint32_t *)(x)))
+#define __REG16(x) (*((volatile uint16_t *)(x)))
+#define __REG8(x) (*((volatile uint8_t *) (x)))
+
+/* Macros used to define a sequence of reserved registers. The start / end */
+/* are byte offsets in the particular register definition, with the "end" */
+/* being the offset of the next un-reserved register. E.g. if offsets */
+/* 0x10 through to 0x1f are reserved, then this reserved area could be */
+/* specified as follows. */
+/* typedef struct */
+/* { */
+/* uint32_t reg1; offset 0x00 */
+/* uint32_t reg2; offset 0x04 */
+/* uint32_t reg3; offset 0x08 */
+/* uint32_t reg4; offset 0x0c */
+/* REG32_RSVD(0x10, 0x20); */
+/* uint32_t reg5; offset 0x20 */
+/* ... */
+/* } EXAMPLE_REG_t; */
+#define REG8_RSVD(start, end) uint8_t rsvd_##start[(end - start) / sizeof(uint8_t)]
+#define REG16_RSVD(start, end) uint16_t rsvd_##start[(end - start) / sizeof(uint16_t)]
+#define REG32_RSVD(start, end) uint32_t rsvd_##start[(end - start) / sizeof(uint32_t)]
+
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+
+/* Note: When protecting multiple statements, the REG_LOCAL_IRQ_SAVE and */
+/* REG_LOCAL_IRQ_RESTORE must be enclosed in { } to allow the */
+/* flags variable to be declared locally. */
+/* e.g. */
+/* statement1; */
+/* { */
+/* REG_LOCAL_IRQ_SAVE; */
+/* <multiple statements here> */
+/* REG_LOCAL_IRQ_RESTORE; */
+/* } */
+/* statement2; */
+/* */
+
+#if defined(__KERNEL__) && !defined(STANDALONE)
+#include <mach/hardware.h>
+#include <linux/interrupt.h>
+
+#define REG_LOCAL_IRQ_SAVE HW_DECLARE_SPINLOCK(reg32) \
+ unsigned long flags; HW_IRQ_SAVE(reg32, flags)
+
+#define REG_LOCAL_IRQ_RESTORE HW_IRQ_RESTORE(reg32, flags)
+
+#else
+
+#define REG_LOCAL_IRQ_SAVE
+#define REG_LOCAL_IRQ_RESTORE
+
+#endif
+
+static inline void reg32_modify_and(volatile uint32_t *reg, uint32_t value)
+{
+ REG_LOCAL_IRQ_SAVE;
+ *reg &= value;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+static inline void reg32_modify_or(volatile uint32_t *reg, uint32_t value)
+{
+ REG_LOCAL_IRQ_SAVE;
+ *reg |= value;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+static inline void reg32_modify_mask(volatile uint32_t *reg, uint32_t mask,
+ uint32_t value)
+{
+ REG_LOCAL_IRQ_SAVE;
+ *reg = (*reg & mask) | value;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+static inline void reg32_write(volatile uint32_t *reg, uint32_t value)
+{
+ *reg = value;
+}
+
+#endif /* CSP_REG_H */
diff --git a/arch/arm/mach-bcmring/include/csp/secHw.h b/arch/arm/mach-bcmring/include/csp/secHw.h
new file mode 100644
index 00000000000..b9d7e0732df
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/csp/secHw.h
@@ -0,0 +1,65 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file secHw.h
+*
+* @brief Definitions for accessing low level security features
+*
+*/
+/****************************************************************************/
+#ifndef SECHW_H
+#define SECHW_H
+
+typedef void (*secHw_FUNC_t) (void);
+
+typedef enum {
+ secHw_MODE_SECURE = 0x0, /* Switches processor into secure mode */
+ secHw_MODE_NONSECURE = 0x1 /* Switches processor into non-secure mode */
+} secHw_MODE;
+
+/****************************************************************************/
+/**
+* @brief Requesting to execute the function in secure mode
+*
+* This function requests the given function to run in secure mode
+*
+*/
+/****************************************************************************/
+void secHw_RunSecure(secHw_FUNC_t /* Function to run in secure mode */
+ );
+
+/****************************************************************************/
+/**
+* @brief Sets the mode
+*
+* his function sets the processor mode (secure/non-secure)
+*
+*/
+/****************************************************************************/
+void secHw_SetMode(secHw_MODE /* Processor mode */
+ );
+
+/****************************************************************************/
+/**
+* @brief Get the current mode
+*
+* This function retieves the processor mode (secure/non-secure)
+*
+*/
+/****************************************************************************/
+void secHw_GetMode(secHw_MODE *);
+
+#endif /* SECHW_H */
diff --git a/arch/arm/mach-bcmring/include/csp/stdint.h b/arch/arm/mach-bcmring/include/csp/stdint.h
new file mode 100644
index 00000000000..3a8718bbf70
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/csp/stdint.h
@@ -0,0 +1,30 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#ifndef CSP_STDINT_H
+#define CSP_STDINT_H
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#else
+#include <stdint.h>
+#endif
+
+/* ---- Public Constants and Types --------------------------------------- */
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+
+#endif /* CSP_STDINT_H */
diff --git a/arch/arm/mach-bcmring/include/csp/string.h b/arch/arm/mach-bcmring/include/csp/string.h
new file mode 100644
index 00000000000..ad9e4005f14
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/csp/string.h
@@ -0,0 +1,34 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+
+
+#ifndef CSP_STRING_H
+#define CSP_STRING_H
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#ifdef __KERNEL__
+ #include <linux/string.h>
+#else
+ #include <string.h>
+#endif
+
+/* ---- Public Constants and Types --------------------------------------- */
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+
+
+#endif /* CSP_STRING_H */
+
diff --git a/arch/arm/mach-bcmring/include/csp/tmrHw.h b/arch/arm/mach-bcmring/include/csp/tmrHw.h
new file mode 100644
index 00000000000..f1236d00cb9
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/csp/tmrHw.h
@@ -0,0 +1,263 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file tmrHw.h
+*
+* @brief API definitions for low level Timer driver
+*
+*/
+/****************************************************************************/
+#ifndef _TMRHW_H
+#define _TMRHW_H
+
+#include <csp/stdint.h>
+
+typedef uint32_t tmrHw_ID_t; /* Timer ID */
+typedef uint32_t tmrHw_COUNT_t; /* Timer count */
+typedef uint32_t tmrHw_INTERVAL_t; /* Timer interval */
+typedef uint32_t tmrHw_RATE_t; /* Timer event (count/interrupt) rate */
+
+typedef enum {
+ tmrHw_INTERRUPT_STATUS_SET, /* Interrupted */
+ tmrHw_INTERRUPT_STATUS_UNSET /* No Interrupt */
+} tmrHw_INTERRUPT_STATUS_e;
+
+typedef enum {
+ tmrHw_CAPABILITY_CLOCK, /* Clock speed in HHz */
+ tmrHw_CAPABILITY_RESOLUTION /* Timer resolution in bits */
+} tmrHw_CAPABILITY_e;
+
+/****************************************************************************/
+/**
+* @brief Get timer capability
+*
+* This function returns various capabilities/attributes of a timer
+*
+* @return Numeric capability
+*
+*/
+/****************************************************************************/
+uint32_t tmrHw_getTimerCapability(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
+ tmrHw_CAPABILITY_e capability /* [ IN ] Timer capability */
+);
+
+/****************************************************************************/
+/**
+* @brief Configures a periodic timer in terms of timer interrupt rate
+*
+* This function initializes a periodic timer to generate specific number of
+* timer interrupt per second
+*
+* @return On success: Effective timer frequency
+* On failure: 0
+*
+*/
+/****************************************************************************/
+tmrHw_RATE_t tmrHw_setPeriodicTimerRate(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
+ tmrHw_RATE_t rate /* [ IN ] Number of timer interrupt per second */
+);
+
+/****************************************************************************/
+/**
+* @brief Configures a periodic timer to generate timer interrupt after
+* certain time interval
+*
+* This function initializes a periodic timer to generate timer interrupt
+* after every time interval in milisecond
+*
+* @return On success: Effective interval set in mili-second
+* On failure: 0
+*
+*/
+/****************************************************************************/
+tmrHw_INTERVAL_t tmrHw_setPeriodicTimerInterval(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
+ tmrHw_INTERVAL_t msec /* [ IN ] Interval in mili-second */
+);
+
+/****************************************************************************/
+/**
+* @brief Configures a periodic timer to generate timer interrupt just once
+* after certain time interval
+*
+* This function initializes a periodic timer to generate a single ticks after
+* certain time interval in milisecond
+*
+* @return On success: Effective interval set in mili-second
+* On failure: 0
+*
+*/
+/****************************************************************************/
+tmrHw_INTERVAL_t tmrHw_setOneshotTimerInterval(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
+ tmrHw_INTERVAL_t msec /* [ IN ] Interval in mili-second */
+);
+
+/****************************************************************************/
+/**
+* @brief Configures a timer to run as a free running timer
+*
+* This function initializes a timer to run as a free running timer
+*
+* @return Timer resolution (count / sec)
+*
+*/
+/****************************************************************************/
+tmrHw_RATE_t tmrHw_setFreeRunningTimer(tmrHw_ID_t timerId, /* [ IN ] Timer Id */
+ uint32_t divider /* [ IN ] Dividing the clock frequency */
+) __attribute__ ((section(".aramtext")));
+
+/****************************************************************************/
+/**
+* @brief Starts a timer
+*
+* This function starts a preconfigured timer
+*
+* @return -1 - On Failure
+* 0 - On Success
+*/
+/****************************************************************************/
+int tmrHw_startTimer(tmrHw_ID_t timerId /* [ IN ] Timer id */
+) __attribute__ ((section(".aramtext")));
+
+/****************************************************************************/
+/**
+* @brief Stops a timer
+*
+* This function stops a running timer
+*
+* @return -1 - On Failure
+* 0 - On Success
+*/
+/****************************************************************************/
+int tmrHw_stopTimer(tmrHw_ID_t timerId /* [ IN ] Timer id */
+);
+
+/****************************************************************************/
+/**
+* @brief Gets current timer count
+*
+* This function returns the current timer value
+*
+* @return Current downcounting timer value
+*
+*/
+/****************************************************************************/
+tmrHw_COUNT_t tmrHw_GetCurrentCount(tmrHw_ID_t timerId /* [ IN ] Timer id */
+) __attribute__ ((section(".aramtext")));
+
+/****************************************************************************/
+/**
+* @brief Gets timer count rate
+*
+* This function returns the number of counts per second
+*
+* @return Count rate
+*
+*/
+/****************************************************************************/
+tmrHw_RATE_t tmrHw_getCountRate(tmrHw_ID_t timerId /* [ IN ] Timer id */
+) __attribute__ ((section(".aramtext")));
+
+/****************************************************************************/
+/**
+* @brief Enables timer interrupt
+*
+* This function enables the timer interrupt
+*
+* @return N/A
+*
+*/
+/****************************************************************************/
+void tmrHw_enableInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */
+);
+
+/****************************************************************************/
+/**
+* @brief Disables timer interrupt
+*
+* This function disable the timer interrupt
+*
+* @return N/A
+*/
+/****************************************************************************/
+void tmrHw_disableInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */
+);
+
+/****************************************************************************/
+/**
+* @brief Clears the interrupt
+*
+* This function clears the timer interrupt
+*
+* @return N/A
+*
+* @note
+* Must be called under the context of ISR
+*/
+/****************************************************************************/
+void tmrHw_clearInterrupt(tmrHw_ID_t timerId /* [ IN ] Timer id */
+);
+
+/****************************************************************************/
+/**
+* @brief Gets the interrupt status
+*
+* This function returns timer interrupt status
+*
+* @return Interrupt status
+*/
+/****************************************************************************/
+tmrHw_INTERRUPT_STATUS_e tmrHw_getInterruptStatus(tmrHw_ID_t timerId /* [ IN ] Timer id */
+);
+
+/****************************************************************************/
+/**
+* @brief Indentifies a timer causing interrupt
+*
+* This functions returns a timer causing interrupt
+*
+* @return 0xFFFFFFFF : No timer causing an interrupt
+* ! 0xFFFFFFFF : timer causing an interrupt
+* @note
+* tmrHw_clearIntrrupt() must be called with a valid timer id after calling this function
+*/
+/****************************************************************************/
+tmrHw_ID_t tmrHw_getInterruptSource(void);
+
+/****************************************************************************/
+/**
+* @brief Displays specific timer registers
+*
+*
+* @return void
+*
+*/
+/****************************************************************************/
+void tmrHw_printDebugInfo(tmrHw_ID_t timerId, /* [ IN ] Timer id */
+ int (*fpPrint) (const char *, ...) /* [ IN ] Print callback function */
+);
+
+/****************************************************************************/
+/**
+* @brief Use a timer to perform a busy wait delay for a number of usecs.
+*
+* @return N/A
+*/
+/****************************************************************************/
+void tmrHw_udelay(tmrHw_ID_t timerId, /* [ IN ] Timer id */
+ unsigned long usecs /* [ IN ] usec to delay */
+) __attribute__ ((section(".aramtext")));
+
+#endif /* _TMRHW_H */
diff --git a/arch/arm/mach-bcmring/include/mach/clkdev.h b/arch/arm/mach-bcmring/include/mach/clkdev.h
new file mode 100644
index 00000000000..04b37a89801
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/clkdev.h
@@ -0,0 +1,7 @@
+#ifndef __ASM_MACH_CLKDEV_H
+#define __ASM_MACH_CLKDEV_H
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+#endif
diff --git a/arch/arm/mach-bcmring/include/mach/csp/cap.h b/arch/arm/mach-bcmring/include/mach/csp/cap.h
new file mode 100644
index 00000000000..30fa2d54063
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/cap.h
@@ -0,0 +1,63 @@
+/*****************************************************************************
+* Copyright 2009 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#ifndef CAP_H
+#define CAP_H
+
+/* ---- Include Files ---------------------------------------------------- */
+/* ---- Public Constants and Types --------------------------------------- */
+typedef enum {
+ CAP_NOT_PRESENT = 0,
+ CAP_PRESENT
+} CAP_RC_T;
+
+typedef enum {
+ CAP_VPM,
+ CAP_ETH_PHY,
+ CAP_ETH_GMII,
+ CAP_ETH_SGMII,
+ CAP_USB,
+ CAP_TSC,
+ CAP_EHSS,
+ CAP_SDIO,
+ CAP_UARTB,
+ CAP_KEYPAD,
+ CAP_CLCD,
+ CAP_GE,
+ CAP_LEDM,
+ CAP_BBL,
+ CAP_VDEC,
+ CAP_PIF,
+ CAP_APM,
+ CAP_SPU,
+ CAP_PKA,
+ CAP_RNG,
+} CAP_CAPABILITY_T;
+
+typedef enum {
+ CAP_LCD_WVGA = 0,
+ CAP_LCD_VGA = 0x1,
+ CAP_LCD_WQVGA = 0x2,
+ CAP_LCD_QVGA = 0x3
+} CAP_LCD_RES_T;
+
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+
+static inline CAP_RC_T cap_isPresent(CAP_CAPABILITY_T capability, int index);
+static inline uint32_t cap_getMaxArmSpeedHz(void);
+static inline uint32_t cap_getMaxVpmSpeedHz(void);
+static inline CAP_LCD_RES_T cap_getMaxLcdRes(void);
+
+#endif
diff --git a/arch/arm/mach-bcmring/include/mach/csp/cap_inline.h b/arch/arm/mach-bcmring/include/mach/csp/cap_inline.h
new file mode 100644
index 00000000000..933ce68ed90
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/cap_inline.h
@@ -0,0 +1,409 @@
+/*****************************************************************************
+* Copyright 2009 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#ifndef CAP_INLINE_H
+#define CAP_INLINE_H
+
+/* ---- Include Files ---------------------------------------------------- */
+#include <mach/csp/cap.h>
+#include <cfg_global.h>
+
+/* ---- Public Constants and Types --------------------------------------- */
+#define CAP_CONFIG0_VPM_DIS 0x00000001
+#define CAP_CONFIG0_ETH_PHY0_DIS 0x00000002
+#define CAP_CONFIG0_ETH_PHY1_DIS 0x00000004
+#define CAP_CONFIG0_ETH_GMII0_DIS 0x00000008
+#define CAP_CONFIG0_ETH_GMII1_DIS 0x00000010
+#define CAP_CONFIG0_ETH_SGMII0_DIS 0x00000020
+#define CAP_CONFIG0_ETH_SGMII1_DIS 0x00000040
+#define CAP_CONFIG0_USB0_DIS 0x00000080
+#define CAP_CONFIG0_USB1_DIS 0x00000100
+#define CAP_CONFIG0_TSC_DIS 0x00000200
+#define CAP_CONFIG0_EHSS0_DIS 0x00000400
+#define CAP_CONFIG0_EHSS1_DIS 0x00000800
+#define CAP_CONFIG0_SDIO0_DIS 0x00001000
+#define CAP_CONFIG0_SDIO1_DIS 0x00002000
+#define CAP_CONFIG0_UARTB_DIS 0x00004000
+#define CAP_CONFIG0_KEYPAD_DIS 0x00008000
+#define CAP_CONFIG0_CLCD_DIS 0x00010000
+#define CAP_CONFIG0_GE_DIS 0x00020000
+#define CAP_CONFIG0_LEDM_DIS 0x00040000
+#define CAP_CONFIG0_BBL_DIS 0x00080000
+#define CAP_CONFIG0_VDEC_DIS 0x00100000
+#define CAP_CONFIG0_PIF_DIS 0x00200000
+#define CAP_CONFIG0_RESERVED1_DIS 0x00400000
+#define CAP_CONFIG0_RESERVED2_DIS 0x00800000
+
+#define CAP_CONFIG1_APMA_DIS 0x00000001
+#define CAP_CONFIG1_APMB_DIS 0x00000002
+#define CAP_CONFIG1_APMC_DIS 0x00000004
+#define CAP_CONFIG1_CLCD_RES_MASK 0x00000600
+#define CAP_CONFIG1_CLCD_RES_SHIFT 9
+#define CAP_CONFIG1_CLCD_RES_WVGA (CAP_LCD_WVGA << CAP_CONFIG1_CLCD_RES_SHIFT)
+#define CAP_CONFIG1_CLCD_RES_VGA (CAP_LCD_VGA << CAP_CONFIG1_CLCD_RES_SHIFT)
+#define CAP_CONFIG1_CLCD_RES_WQVGA (CAP_LCD_WQVGA << CAP_CONFIG1_CLCD_RES_SHIFT)
+#define CAP_CONFIG1_CLCD_RES_QVGA (CAP_LCD_QVGA << CAP_CONFIG1_CLCD_RES_SHIFT)
+
+#define CAP_CONFIG2_SPU_DIS 0x00000010
+#define CAP_CONFIG2_PKA_DIS 0x00000020
+#define CAP_CONFIG2_RNG_DIS 0x00000080
+
+#if (CFG_GLOBAL_CHIP == BCM11107)
+#define capConfig0 0
+#define capConfig1 CAP_CONFIG1_CLCD_RES_WVGA
+#define capConfig2 0
+#define CAP_APM_MAX_NUM_CHANS 3
+#elif (CFG_GLOBAL_CHIP == FPGA11107)
+#define capConfig0 0
+#define capConfig1 CAP_CONFIG1_CLCD_RES_WVGA
+#define capConfig2 0
+#define CAP_APM_MAX_NUM_CHANS 3
+#elif (CFG_GLOBAL_CHIP == BCM11109)
+#define capConfig0 (CAP_CONFIG0_USB1_DIS | CAP_CONFIG0_EHSS1_DIS | CAP_CONFIG0_SDIO1_DIS | CAP_CONFIG0_GE_DIS | CAP_CONFIG0_BBL_DIS | CAP_CONFIG0_VDEC_DIS)
+#define capConfig1 (CAP_CONFIG1_APMC_DIS | CAP_CONFIG1_CLCD_RES_WQVGA)
+#define capConfig2 (CAP_CONFIG2_SPU_DIS | CAP_CONFIG2_PKA_DIS)
+#define CAP_APM_MAX_NUM_CHANS 2
+#elif (CFG_GLOBAL_CHIP == BCM11170)
+#define capConfig0 (CAP_CONFIG0_ETH_GMII0_DIS | CAP_CONFIG0_ETH_GMII1_DIS | CAP_CONFIG0_USB0_DIS | CAP_CONFIG0_USB1_DIS | CAP_CONFIG0_TSC_DIS | CAP_CONFIG0_EHSS1_DIS | CAP_CONFIG0_SDIO0_DIS | CAP_CONFIG0_SDIO1_DIS | CAP_CONFIG0_UARTB_DIS | CAP_CONFIG0_CLCD_DIS | CAP_CONFIG0_GE_DIS | CAP_CONFIG0_BBL_DIS | CAP_CONFIG0_VDEC_DIS)
+#define capConfig1 (CAP_CONFIG1_APMC_DIS | CAP_CONFIG1_CLCD_RES_WQVGA)
+#define capConfig2 (CAP_CONFIG2_SPU_DIS | CAP_CONFIG2_PKA_DIS)
+#define CAP_APM_MAX_NUM_CHANS 2
+#elif (CFG_GLOBAL_CHIP == BCM11110)
+#define capConfig0 (CAP_CONFIG0_USB1_DIS | CAP_CONFIG0_TSC_DIS | CAP_CONFIG0_EHSS1_DIS | CAP_CONFIG0_SDIO0_DIS | CAP_CONFIG0_SDIO1_DIS | CAP_CONFIG0_UARTB_DIS | CAP_CONFIG0_GE_DIS | CAP_CONFIG0_BBL_DIS | CAP_CONFIG0_VDEC_DIS)
+#define capConfig1 CAP_CONFIG1_APMC_DIS
+#define capConfig2 (CAP_CONFIG2_SPU_DIS | CAP_CONFIG2_PKA_DIS)
+#define CAP_APM_MAX_NUM_CHANS 2
+#elif (CFG_GLOBAL_CHIP == BCM11211)
+#define capConfig0 (CAP_CONFIG0_ETH_PHY0_DIS | CAP_CONFIG0_ETH_GMII0_DIS | CAP_CONFIG0_ETH_GMII1_DIS | CAP_CONFIG0_ETH_SGMII0_DIS | CAP_CONFIG0_ETH_SGMII1_DIS | CAP_CONFIG0_CLCD_DIS)
+#define capConfig1 CAP_CONFIG1_APMC_DIS
+#define capConfig2 0
+#define CAP_APM_MAX_NUM_CHANS 2
+#else
+#error CFG_GLOBAL_CHIP type capabilities not defined
+#endif
+
+#if ((CFG_GLOBAL_CHIP == BCM11107) || (CFG_GLOBAL_CHIP == FPGA11107))
+#define CAP_HW_CFG_ARM_CLK_HZ 500000000
+#elif ((CFG_GLOBAL_CHIP == BCM11109) || (CFG_GLOBAL_CHIP == BCM11170) || (CFG_GLOBAL_CHIP == BCM11110))
+#define CAP_HW_CFG_ARM_CLK_HZ 300000000
+#elif (CFG_GLOBAL_CHIP == BCM11211)
+#define CAP_HW_CFG_ARM_CLK_HZ 666666666
+#else
+#error CFG_GLOBAL_CHIP type capabilities not defined
+#endif
+
+#if ((CFG_GLOBAL_CHIP == BCM11107) || (CFG_GLOBAL_CHIP == BCM11211) || (CFG_GLOBAL_CHIP == FPGA11107))
+#define CAP_HW_CFG_VPM_CLK_HZ 333333333
+#elif ((CFG_GLOBAL_CHIP == BCM11109) || (CFG_GLOBAL_CHIP == BCM11170) || (CFG_GLOBAL_CHIP == BCM11110))
+#define CAP_HW_CFG_VPM_CLK_HZ 200000000
+#else
+#error CFG_GLOBAL_CHIP type capabilities not defined
+#endif
+
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+
+/****************************************************************************
+* cap_isPresent -
+*
+* PURPOSE:
+* Determines if the chip has a certain capability present
+*
+* PARAMETERS:
+* capability - type of capability to determine if present
+*
+* RETURNS:
+* CAP_PRESENT or CAP_NOT_PRESENT
+****************************************************************************/
+static inline CAP_RC_T cap_isPresent(CAP_CAPABILITY_T capability, int index)
+{
+ CAP_RC_T returnVal = CAP_NOT_PRESENT;
+
+ switch (capability) {
+ case CAP_VPM:
+ {
+ if (!(capConfig0 & CAP_CONFIG0_VPM_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_ETH_PHY:
+ {
+ if ((index == 0)
+ && (!(capConfig0 & CAP_CONFIG0_ETH_PHY0_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ if ((index == 1)
+ && (!(capConfig0 & CAP_CONFIG0_ETH_PHY1_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_ETH_GMII:
+ {
+ if ((index == 0)
+ && (!(capConfig0 & CAP_CONFIG0_ETH_GMII0_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ if ((index == 1)
+ && (!(capConfig0 & CAP_CONFIG0_ETH_GMII1_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_ETH_SGMII:
+ {
+ if ((index == 0)
+ && (!(capConfig0 & CAP_CONFIG0_ETH_SGMII0_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ if ((index == 1)
+ && (!(capConfig0 & CAP_CONFIG0_ETH_SGMII1_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_USB:
+ {
+ if ((index == 0)
+ && (!(capConfig0 & CAP_CONFIG0_USB0_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ if ((index == 1)
+ && (!(capConfig0 & CAP_CONFIG0_USB1_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_TSC:
+ {
+ if (!(capConfig0 & CAP_CONFIG0_TSC_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_EHSS:
+ {
+ if ((index == 0)
+ && (!(capConfig0 & CAP_CONFIG0_EHSS0_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ if ((index == 1)
+ && (!(capConfig0 & CAP_CONFIG0_EHSS1_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_SDIO:
+ {
+ if ((index == 0)
+ && (!(capConfig0 & CAP_CONFIG0_SDIO0_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ if ((index == 1)
+ && (!(capConfig0 & CAP_CONFIG0_SDIO1_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_UARTB:
+ {
+ if (!(capConfig0 & CAP_CONFIG0_UARTB_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_KEYPAD:
+ {
+ if (!(capConfig0 & CAP_CONFIG0_KEYPAD_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_CLCD:
+ {
+ if (!(capConfig0 & CAP_CONFIG0_CLCD_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_GE:
+ {
+ if (!(capConfig0 & CAP_CONFIG0_GE_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_LEDM:
+ {
+ if (!(capConfig0 & CAP_CONFIG0_LEDM_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_BBL:
+ {
+ if (!(capConfig0 & CAP_CONFIG0_BBL_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_VDEC:
+ {
+ if (!(capConfig0 & CAP_CONFIG0_VDEC_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_PIF:
+ {
+ if (!(capConfig0 & CAP_CONFIG0_PIF_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_APM:
+ {
+ if ((index == 0)
+ && (!(capConfig1 & CAP_CONFIG1_APMA_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ if ((index == 1)
+ && (!(capConfig1 & CAP_CONFIG1_APMB_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ if ((index == 2)
+ && (!(capConfig1 & CAP_CONFIG1_APMC_DIS))) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_SPU:
+ {
+ if (!(capConfig2 & CAP_CONFIG2_SPU_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_PKA:
+ {
+ if (!(capConfig2 & CAP_CONFIG2_PKA_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ case CAP_RNG:
+ {
+ if (!(capConfig2 & CAP_CONFIG2_RNG_DIS)) {
+ returnVal = CAP_PRESENT;
+ }
+ }
+ break;
+
+ default:
+ {
+ }
+ break;
+ }
+ return returnVal;
+}
+
+/****************************************************************************
+* cap_getMaxArmSpeedHz -
+*
+* PURPOSE:
+* Determines the maximum speed of the ARM CPU
+*
+* PARAMETERS:
+* none
+*
+* RETURNS:
+* clock speed in Hz that the ARM processor is able to run at
+****************************************************************************/
+static inline uint32_t cap_getMaxArmSpeedHz(void)
+{
+#if ((CFG_GLOBAL_CHIP == BCM11107) || (CFG_GLOBAL_CHIP == FPGA11107))
+ return 500000000;
+#elif ((CFG_GLOBAL_CHIP == BCM11109) || (CFG_GLOBAL_CHIP == BCM11170) || (CFG_GLOBAL_CHIP == BCM11110))
+ return 300000000;
+#elif (CFG_GLOBAL_CHIP == BCM11211)
+ return 666666666;
+#else
+#error CFG_GLOBAL_CHIP type capabilities not defined
+#endif
+}
+
+/****************************************************************************
+* cap_getMaxVpmSpeedHz -
+*
+* PURPOSE:
+* Determines the maximum speed of the VPM
+*
+* PARAMETERS:
+* none
+*
+* RETURNS:
+* clock speed in Hz that the VPM is able to run at
+****************************************************************************/
+static inline uint32_t cap_getMaxVpmSpeedHz(void)
+{
+#if ((CFG_GLOBAL_CHIP == BCM11107) || (CFG_GLOBAL_CHIP == BCM11211) || (CFG_GLOBAL_CHIP == FPGA11107))
+ return 333333333;
+#elif ((CFG_GLOBAL_CHIP == BCM11109) || (CFG_GLOBAL_CHIP == BCM11170) || (CFG_GLOBAL_CHIP == BCM11110))
+ return 200000000;
+#else
+#error CFG_GLOBAL_CHIP type capabilities not defined
+#endif
+}
+
+/****************************************************************************
+* cap_getMaxLcdRes -
+*
+* PURPOSE:
+* Determines the maximum LCD resolution capabilities
+*
+* PARAMETERS:
+* none
+*
+* RETURNS:
+* CAP_LCD_WVGA, CAP_LCD_VGA, CAP_LCD_WQVGA or CAP_LCD_QVGA
+*
+****************************************************************************/
+static inline CAP_LCD_RES_T cap_getMaxLcdRes(void)
+{
+ return (CAP_LCD_RES_T)
+ ((capConfig1 & CAP_CONFIG1_CLCD_RES_MASK) >>
+ CAP_CONFIG1_CLCD_RES_SHIFT);
+}
+
+#endif
diff --git a/arch/arm/mach-bcmring/include/mach/csp/chipcHw_def.h b/arch/arm/mach-bcmring/include/mach/csp/chipcHw_def.h
new file mode 100644
index 00000000000..70eaea866cf
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/chipcHw_def.h
@@ -0,0 +1,1123 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#ifndef CHIPC_DEF_H
+#define CHIPC_DEF_H
+
+/* ---- Include Files ----------------------------------------------------- */
+
+#include <csp/stdint.h>
+#include <csp/errno.h>
+#include <csp/reg.h>
+#include <mach/csp/chipcHw_reg.h>
+
+/* ---- Public Constants and Types ---------------------------------------- */
+
+/* Set 1 to configure DDR/VPM phase alignment by HW */
+#define chipcHw_DDR_HW_PHASE_ALIGN 0
+#define chipcHw_VPM_HW_PHASE_ALIGN 0
+
+typedef uint32_t chipcHw_freq;
+
+/* Configurable miscellaneous clocks */
+typedef enum {
+ chipcHw_CLOCK_DDR, /* DDR PHY Clock */
+ chipcHw_CLOCK_ARM, /* ARM Clock */
+ chipcHw_CLOCK_ESW, /* Ethernet Switch Clock */
+ chipcHw_CLOCK_VPM, /* VPM Clock */
+ chipcHw_CLOCK_ESW125, /* Ethernet MII Clock */
+ chipcHw_CLOCK_UART, /* UART Clock */
+ chipcHw_CLOCK_SDIO0, /* SDIO 0 Clock */
+ chipcHw_CLOCK_SDIO1, /* SDIO 1 Clock */
+ chipcHw_CLOCK_SPI, /* SPI Clock */
+ chipcHw_CLOCK_ETM, /* ARM ETM Clock */
+
+ chipcHw_CLOCK_BUS, /* BUS Clock */
+ chipcHw_CLOCK_OTP, /* OTP Clock */
+ chipcHw_CLOCK_I2C, /* I2C Host Clock */
+ chipcHw_CLOCK_I2S0, /* I2S 0 Host Clock */
+ chipcHw_CLOCK_RTBUS, /* DDR PHY Configuration Clock */
+ chipcHw_CLOCK_APM100, /* APM100 Clock */
+ chipcHw_CLOCK_TSC, /* Touch screen Clock */
+ chipcHw_CLOCK_LED, /* LED Clock */
+
+ chipcHw_CLOCK_USB, /* USB Clock */
+ chipcHw_CLOCK_LCD, /* LCD CLock */
+ chipcHw_CLOCK_APM, /* APM Clock */
+
+ chipcHw_CLOCK_I2S1, /* I2S 1 Host Clock */
+} chipcHw_CLOCK_e;
+
+/* System booting strap options */
+typedef enum {
+ chipcHw_BOOT_DEVICE_UART = chipcHw_STRAPS_BOOT_DEVICE_UART,
+ chipcHw_BOOT_DEVICE_SERIAL_FLASH =
+ chipcHw_STRAPS_BOOT_DEVICE_SERIAL_FLASH,
+ chipcHw_BOOT_DEVICE_NOR_FLASH_16 =
+ chipcHw_STRAPS_BOOT_DEVICE_NOR_FLASH_16,
+ chipcHw_BOOT_DEVICE_NAND_FLASH_8 =
+ chipcHw_STRAPS_BOOT_DEVICE_NAND_FLASH_8,
+ chipcHw_BOOT_DEVICE_NAND_FLASH_16 =
+ chipcHw_STRAPS_BOOT_DEVICE_NAND_FLASH_16
+} chipcHw_BOOT_DEVICE_e;
+
+/* System booting modes */
+typedef enum {
+ chipcHw_BOOT_MODE_NORMAL = chipcHw_STRAPS_BOOT_MODE_NORMAL,
+ chipcHw_BOOT_MODE_DBG_SW = chipcHw_STRAPS_BOOT_MODE_DBG_SW,
+ chipcHw_BOOT_MODE_DBG_BOOT = chipcHw_STRAPS_BOOT_MODE_DBG_BOOT,
+ chipcHw_BOOT_MODE_NORMAL_QUIET = chipcHw_STRAPS_BOOT_MODE_NORMAL_QUIET
+} chipcHw_BOOT_MODE_e;
+
+/* NAND Flash page size strap options */
+typedef enum {
+ chipcHw_NAND_PAGESIZE_512 = chipcHw_STRAPS_NAND_PAGESIZE_512,
+ chipcHw_NAND_PAGESIZE_2048 = chipcHw_STRAPS_NAND_PAGESIZE_2048,
+ chipcHw_NAND_PAGESIZE_4096 = chipcHw_STRAPS_NAND_PAGESIZE_4096,
+ chipcHw_NAND_PAGESIZE_EXT = chipcHw_STRAPS_NAND_PAGESIZE_EXT
+} chipcHw_NAND_PAGESIZE_e;
+
+/* GPIO Pin function */
+typedef enum {
+ chipcHw_GPIO_FUNCTION_KEYPAD = chipcHw_REG_GPIO_MUX_KEYPAD,
+ chipcHw_GPIO_FUNCTION_I2CH = chipcHw_REG_GPIO_MUX_I2CH,
+ chipcHw_GPIO_FUNCTION_SPI = chipcHw_REG_GPIO_MUX_SPI,
+ chipcHw_GPIO_FUNCTION_UART = chipcHw_REG_GPIO_MUX_UART,
+ chipcHw_GPIO_FUNCTION_LEDMTXP = chipcHw_REG_GPIO_MUX_LEDMTXP,
+ chipcHw_GPIO_FUNCTION_LEDMTXS = chipcHw_REG_GPIO_MUX_LEDMTXS,
+ chipcHw_GPIO_FUNCTION_SDIO0 = chipcHw_REG_GPIO_MUX_SDIO0,
+ chipcHw_GPIO_FUNCTION_SDIO1 = chipcHw_REG_GPIO_MUX_SDIO1,
+ chipcHw_GPIO_FUNCTION_PCM = chipcHw_REG_GPIO_MUX_PCM,
+ chipcHw_GPIO_FUNCTION_I2S = chipcHw_REG_GPIO_MUX_I2S,
+ chipcHw_GPIO_FUNCTION_ETM = chipcHw_REG_GPIO_MUX_ETM,
+ chipcHw_GPIO_FUNCTION_DEBUG = chipcHw_REG_GPIO_MUX_DEBUG,
+ chipcHw_GPIO_FUNCTION_MISC = chipcHw_REG_GPIO_MUX_MISC,
+ chipcHw_GPIO_FUNCTION_GPIO = chipcHw_REG_GPIO_MUX_GPIO
+} chipcHw_GPIO_FUNCTION_e;
+
+/* PIN Output slew rate */
+typedef enum {
+ chipcHw_PIN_SLEW_RATE_HIGH = chipcHw_REG_SLEW_RATE_HIGH,
+ chipcHw_PIN_SLEW_RATE_NORMAL = chipcHw_REG_SLEW_RATE_NORMAL
+} chipcHw_PIN_SLEW_RATE_e;
+
+/* PIN Current drive strength */
+typedef enum {
+ chipcHw_PIN_CURRENT_STRENGTH_2mA = chipcHw_REG_CURRENT_STRENGTH_2mA,
+ chipcHw_PIN_CURRENT_STRENGTH_4mA = chipcHw_REG_CURRENT_STRENGTH_4mA,
+ chipcHw_PIN_CURRENT_STRENGTH_6mA = chipcHw_REG_CURRENT_STRENGTH_6mA,
+ chipcHw_PIN_CURRENT_STRENGTH_8mA = chipcHw_REG_CURRENT_STRENGTH_8mA,
+ chipcHw_PIN_CURRENT_STRENGTH_10mA = chipcHw_REG_CURRENT_STRENGTH_10mA,
+ chipcHw_PIN_CURRENT_STRENGTH_12mA = chipcHw_REG_CURRENT_STRENGTH_12mA
+} chipcHw_PIN_CURRENT_STRENGTH_e;
+
+/* PIN Pull up register settings */
+typedef enum {
+ chipcHw_PIN_PULL_NONE = chipcHw_REG_PULL_NONE,
+ chipcHw_PIN_PULL_UP = chipcHw_REG_PULL_UP,
+ chipcHw_PIN_PULL_DOWN = chipcHw_REG_PULL_DOWN
+} chipcHw_PIN_PULL_e;
+
+/* PIN input type settings */
+typedef enum {
+ chipcHw_PIN_INPUTTYPE_CMOS = chipcHw_REG_INPUTTYPE_CMOS,
+ chipcHw_PIN_INPUTTYPE_ST = chipcHw_REG_INPUTTYPE_ST
+} chipcHw_PIN_INPUTTYPE_e;
+
+/* Allow/Disalow the support of spread spectrum */
+typedef enum {
+ chipcHw_SPREAD_SPECTRUM_DISALLOW, /* Spread spectrum support is not allowed */
+ chipcHw_SPREAD_SPECTRUM_ALLOW /* Spread spectrum support is allowed */
+} chipcHw_SPREAD_SPECTRUM_e;
+
+typedef struct {
+ chipcHw_SPREAD_SPECTRUM_e ssSupport; /* Allow/Disalow to support spread spectrum.
+ If supported, call chipcHw_enableSpreadSpectrum ()
+ to activate the spread spectrum with desired spread. */
+ uint32_t pllVcoFreqHz; /* PLL VCO frequency in Hz */
+ uint32_t pll2VcoFreqHz; /* PLL2 VCO frequency in Hz */
+ uint32_t busClockFreqHz; /* Bus clock frequency in Hz */
+ uint32_t armBusRatio; /* ARM clock : Bus clock */
+ uint32_t vpmBusRatio; /* VPM clock : Bus clock */
+ uint32_t ddrBusRatio; /* DDR clock : Bus clock */
+} chipcHw_INIT_PARAM_t;
+
+/* CHIP revision number */
+typedef enum {
+ chipcHw_REV_NUMBER_A0 = chipcHw_REG_REV_A0,
+ chipcHw_REV_NUMBER_B0 = chipcHw_REG_REV_B0
+} chipcHw_REV_NUMBER_e;
+
+typedef enum {
+ chipcHw_VPM_HW_PHASE_INTR_DISABLE = chipcHw_REG_VPM_INTR_DISABLE,
+ chipcHw_VPM_HW_PHASE_INTR_FAST = chipcHw_REG_VPM_INTR_FAST,
+ chipcHw_VPM_HW_PHASE_INTR_MEDIUM = chipcHw_REG_VPM_INTR_MEDIUM,
+ chipcHw_VPM_HW_PHASE_INTR_SLOW = chipcHw_REG_VPM_INTR_SLOW
+} chipcHw_VPM_HW_PHASE_INTR_e;
+
+typedef enum {
+ chipcHw_DDR_HW_PHASE_MARGIN_STRICT, /* Strict margin for DDR phase align condition */
+ chipcHw_DDR_HW_PHASE_MARGIN_MEDIUM, /* Medium margin for DDR phase align condition */
+ chipcHw_DDR_HW_PHASE_MARGIN_WIDE /* Wider margin for DDR phase align condition */
+} chipcHw_DDR_HW_PHASE_MARGIN_e;
+
+typedef enum {
+ chipcHw_VPM_HW_PHASE_MARGIN_STRICT, /* Strict margin for VPM phase align condition */
+ chipcHw_VPM_HW_PHASE_MARGIN_MEDIUM, /* Medium margin for VPM phase align condition */
+ chipcHw_VPM_HW_PHASE_MARGIN_WIDE /* Wider margin for VPM phase align condition */
+} chipcHw_VPM_HW_PHASE_MARGIN_e;
+
+#define chipcHw_XTAL_FREQ_Hz 25000000 /* Reference clock frequency in Hz */
+
+/* Programable pin defines */
+#define chipcHw_PIN_GPIO(n) ((((n) >= 0) && ((n) < (chipcHw_GPIO_COUNT))) ? (n) : 0xFFFFFFFF)
+ /* GPIO pin 0 - 60 */
+#define chipcHw_PIN_UARTTXD (chipcHw_GPIO_COUNT + 0) /* UART Transmit */
+#define chipcHw_PIN_NVI_A (chipcHw_GPIO_COUNT + 1) /* NVI Interface */
+#define chipcHw_PIN_NVI_D (chipcHw_GPIO_COUNT + 2) /* NVI Interface */
+#define chipcHw_PIN_NVI_OEB (chipcHw_GPIO_COUNT + 3) /* NVI Interface */
+#define chipcHw_PIN_NVI_WEB (chipcHw_GPIO_COUNT + 4) /* NVI Interface */
+#define chipcHw_PIN_NVI_CS (chipcHw_GPIO_COUNT + 5) /* NVI Interface */
+#define chipcHw_PIN_NVI_NAND_CSB (chipcHw_GPIO_COUNT + 6) /* NVI Interface */
+#define chipcHw_PIN_NVI_FLASHWP (chipcHw_GPIO_COUNT + 7) /* NVI Interface */
+#define chipcHw_PIN_NVI_NAND_RDYB (chipcHw_GPIO_COUNT + 8) /* NVI Interface */
+#define chipcHw_PIN_CL_DATA_0_17 (chipcHw_GPIO_COUNT + 9) /* LCD Data 0 - 17 */
+#define chipcHw_PIN_CL_DATA_18_20 (chipcHw_GPIO_COUNT + 10) /* LCD Data 18 - 20 */
+#define chipcHw_PIN_CL_DATA_21_23 (chipcHw_GPIO_COUNT + 11) /* LCD Data 21 - 23 */
+#define chipcHw_PIN_CL_POWER (chipcHw_GPIO_COUNT + 12) /* LCD Power */
+#define chipcHw_PIN_CL_ACK (chipcHw_GPIO_COUNT + 13) /* LCD Ack */
+#define chipcHw_PIN_CL_FP (chipcHw_GPIO_COUNT + 14) /* LCD FP */
+#define chipcHw_PIN_CL_LP (chipcHw_GPIO_COUNT + 15) /* LCD LP */
+#define chipcHw_PIN_UARTRXD (chipcHw_GPIO_COUNT + 16) /* UART Receive */
+
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+
+/****************************************************************************/
+/**
+* @brief Initializes the clock module
+*
+*/
+/****************************************************************************/
+void chipcHw_Init(chipcHw_INIT_PARAM_t *initParam /* [ IN ] Misc chip initialization parameter */
+ ) __attribute__ ((section(".aramtext")));
+
+/****************************************************************************/
+/**
+* @brief Enables the PLL1
+*
+* This function enables the PLL1
+*
+*/
+/****************************************************************************/
+void chipcHw_pll1Enable(uint32_t vcoFreqHz, /* [ IN ] VCO frequency in Hz */
+ chipcHw_SPREAD_SPECTRUM_e ssSupport /* [ IN ] SS status */
+ ) __attribute__ ((section(".aramtext")));
+
+/****************************************************************************/
+/**
+* @brief Enables the PLL2
+*
+* This function enables the PLL2
+*
+*/
+/****************************************************************************/
+void chipcHw_pll2Enable(uint32_t vcoFreqHz /* [ IN ] VCO frequency in Hz */
+ ) __attribute__ ((section(".aramtext")));
+
+/****************************************************************************/
+/**
+* @brief Disable the PLL1
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_pll1Disable(void);
+
+/****************************************************************************/
+/**
+* @brief Disable the PLL2
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_pll2Disable(void);
+
+/****************************************************************************/
+/**
+* @brief Set clock fequency for miscellaneous configurable clocks
+*
+* This function sets clock frequency
+*
+* @return Configured clock frequency in KHz
+*
+*/
+/****************************************************************************/
+chipcHw_freq chipcHw_getClockFrequency(chipcHw_CLOCK_e clock /* [ IN ] Configurable clock */
+ ) __attribute__ ((section(".aramtext")));
+
+/****************************************************************************/
+/**
+* @brief Set clock fequency for miscellaneous configurable clocks
+*
+* This function sets clock frequency
+*
+* @return Configured clock frequency in Hz
+*
+*/
+/****************************************************************************/
+chipcHw_freq chipcHw_setClockFrequency(chipcHw_CLOCK_e clock, /* [ IN ] Configurable clock */
+ uint32_t freq /* [ IN ] Clock frequency in Hz */
+ ) __attribute__ ((section(".aramtext")));
+
+/****************************************************************************/
+/**
+* @brief Set VPM clock in sync with BUS clock
+*
+* This function does the phase adjustment between VPM and BUS clock
+*
+* @return >= 0 : On success ( # of adjustment required )
+* -1 : On failure
+*/
+/****************************************************************************/
+int chipcHw_vpmPhaseAlign(void);
+
+/****************************************************************************/
+/**
+* @brief Enables core a clock of a certain device
+*
+* This function enables a core clock
+*
+* @return void
+*
+* @note Doesnot affect the bus interface clock
+*/
+/****************************************************************************/
+static inline void chipcHw_setClockEnable(chipcHw_CLOCK_e clock /* [ IN ] Configurable clock */
+ );
+
+/****************************************************************************/
+/**
+* @brief Disabled a core clock of a certain device
+*
+* This function disables a core clock
+*
+* @return void
+*
+* @note Doesnot affect the bus interface clock
+*/
+/****************************************************************************/
+static inline void chipcHw_setClockDisable(chipcHw_CLOCK_e clock /* [ IN ] Configurable clock */
+ );
+
+/****************************************************************************/
+/**
+* @brief Enables bypass clock of a certain device
+*
+* This function enables bypass clock
+*
+* @note Doesnot affect the bus interface clock
+*/
+/****************************************************************************/
+static inline void chipcHw_bypassClockEnable(chipcHw_CLOCK_e clock /* [ IN ] Configurable clock */
+ );
+
+/****************************************************************************/
+/**
+* @brief Disabled bypass clock of a certain device
+*
+* This function disables bypass clock
+*
+* @note Doesnot affect the bus interface clock
+*/
+/****************************************************************************/
+static inline void chipcHw_bypassClockDisable(chipcHw_CLOCK_e clock /* [ IN ] Configurable clock */
+ );
+
+/****************************************************************************/
+/**
+* @brief Get Numeric Chip ID
+*
+* This function returns Chip ID that includes the revison number
+*
+* @return Complete numeric Chip ID
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getChipId(void);
+
+/****************************************************************************/
+/**
+* @brief Get Chip Product ID
+*
+* This function returns Chip Product ID
+*
+* @return Chip Product ID
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getChipProductId(void);
+
+/****************************************************************************/
+/**
+* @brief Get revision number
+*
+* This function returns revision number of the chip
+*
+* @return Revision number
+*/
+/****************************************************************************/
+static inline chipcHw_REV_NUMBER_e chipcHw_getChipRevisionNumber(void);
+
+/****************************************************************************/
+/**
+* @brief Enables bus interface clock
+*
+* Enables bus interface clock of various device
+*
+* @return void
+*
+* @note use chipcHw_REG_BUS_CLOCK_XXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_busInterfaceClockEnable(uint32_t mask /* [ IN ] Bit map of type chipcHw_REG_BUS_CLOCK_XXXXX */
+ );
+
+/****************************************************************************/
+/**
+* @brief Disables bus interface clock
+*
+* Disables bus interface clock of various device
+*
+* @return void
+*
+* @note use chipcHw_REG_BUS_CLOCK_XXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_busInterfaceClockDisable(uint32_t mask /* [ IN ] Bit map of type chipcHw_REG_BUS_CLOCK_XXXXX */
+ );
+
+/****************************************************************************/
+/**
+* @brief Enables various audio channels
+*
+* Enables audio channel
+*
+* @return void
+*
+* @note use chipcHw_REG_AUDIO_CHANNEL_XXXXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_audioChannelEnable(uint32_t mask /* [ IN ] Bit map of type chipcHw_REG_AUDIO_CHANNEL_XXXXXX */
+ );
+
+/****************************************************************************/
+/**
+* @brief Disables various audio channels
+*
+* Disables audio channel
+*
+* @return void
+*
+* @note use chipcHw_REG_AUDIO_CHANNEL_XXXXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_audioChannelDisable(uint32_t mask /* [ IN ] Bit map of type chipcHw_REG_AUDIO_CHANNEL_XXXXXX */
+ );
+
+/****************************************************************************/
+/**
+* @brief Soft resets devices
+*
+* Soft resets various devices
+*
+* @return void
+*
+* @note use chipcHw_REG_SOFT_RESET_XXXXXX defines
+*/
+/****************************************************************************/
+static inline void chipcHw_softReset(uint64_t mask /* [ IN ] Bit map of type chipcHw_REG_SOFT_RESET_XXXXXX */
+ );
+
+static inline void chipcHw_softResetDisable(uint64_t mask /* [ IN ] Bit map of type chipcHw_REG_SOFT_RESET_XXXXXX */
+ );
+
+static inline void chipcHw_softResetEnable(uint64_t mask /* [ IN ] Bit map of type chipcHw_REG_SOFT_RESET_XXXXXX */
+ );
+
+/****************************************************************************/
+/**
+* @brief Configures misc CHIP functionality
+*
+* Configures CHIP functionality
+*
+* @return void
+*
+* @note use chipcHw_REG_MISC_CTRL_XXXXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_miscControl(uint32_t mask /* [ IN ] Bit map of type chipcHw_REG_MISC_CTRL_XXXXXX */
+ );
+
+static inline void chipcHw_miscControlDisable(uint32_t mask /* [ IN ] Bit map of type chipcHw_REG_MISC_CTRL_XXXXXX */
+ );
+
+static inline void chipcHw_miscControlEnable(uint32_t mask /* [ IN ] Bit map of type chipcHw_REG_MISC_CTRL_XXXXXX */
+ );
+
+/****************************************************************************/
+/**
+* @brief Set OTP options
+*
+* Set OTP options
+*
+* @return void
+*
+* @note use chipcHw_REG_OTP_XXXXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_setOTPOption(uint64_t mask /* [ IN ] Bit map of type chipcHw_REG_OTP_XXXXXX */
+ );
+
+/****************************************************************************/
+/**
+* @brief Get sticky bits
+*
+* @return Sticky bit options of type chipcHw_REG_STICKY_XXXXXX
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getStickyBits(void);
+
+/****************************************************************************/
+/**
+* @brief Set sticky bits
+*
+* @return void
+*
+* @note use chipcHw_REG_STICKY_XXXXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_setStickyBits(uint32_t mask /* [ IN ] Bit map of type chipcHw_REG_STICKY_XXXXXX */
+ );
+
+/****************************************************************************/
+/**
+* @brief Clear sticky bits
+*
+* @return void
+*
+* @note use chipcHw_REG_STICKY_XXXXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_clearStickyBits(uint32_t mask /* [ IN ] Bit map of type chipcHw_REG_STICKY_XXXXXX */
+ );
+
+/****************************************************************************/
+/**
+* @brief Get software override strap options
+*
+* Retrieves software override strap options
+*
+* @return Software override strap value
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getSoftStraps(void);
+
+/****************************************************************************/
+/**
+* @brief Set software override strap options
+*
+* set software override strap options
+*
+* @return nothing
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setSoftStraps(uint32_t strapOptions);
+
+/****************************************************************************/
+/**
+* @brief Get pin strap options
+*
+* Retrieves pin strap options
+*
+* @return Pin strap value
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getPinStraps(void);
+
+/****************************************************************************/
+/**
+* @brief Get valid pin strap options
+*
+* Retrieves valid pin strap options
+*
+* @return valid Pin strap value
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getValidStraps(void);
+
+/****************************************************************************/
+/**
+* @brief Initialize valid pin strap options
+*
+* Retrieves valid pin strap options by copying HW strap options to soft register
+* (if chipcHw_STRAPS_SOFT_OVERRIDE not set)
+*
+* @return nothing
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_initValidStraps(void);
+
+/****************************************************************************/
+/**
+* @brief Get status (enabled/disabled) of bus interface clock
+*
+* This function returns the status of devices' bus interface clock
+*
+* @return Bus interface clock
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getBusInterfaceClockStatus(void);
+
+/****************************************************************************/
+/**
+* @brief Get boot device
+*
+* This function returns the device type used in booting the system
+*
+* @return Boot device of type chipcHw_BOOT_DEVICE_e
+*
+*/
+/****************************************************************************/
+static inline chipcHw_BOOT_DEVICE_e chipcHw_getBootDevice(void);
+
+/****************************************************************************/
+/**
+* @brief Get boot mode
+*
+* This function returns the way the system was booted
+*
+* @return Boot mode of type chipcHw_BOOT_MODE_e
+*
+*/
+/****************************************************************************/
+static inline chipcHw_BOOT_MODE_e chipcHw_getBootMode(void);
+
+/****************************************************************************/
+/**
+* @brief Get NAND flash page size
+*
+* This function returns the NAND device page size
+*
+* @return Boot NAND device page size
+*
+*/
+/****************************************************************************/
+static inline chipcHw_NAND_PAGESIZE_e chipcHw_getNandPageSize(void);
+
+/****************************************************************************/
+/**
+* @brief Get NAND flash address cycle configuration
+*
+* This function returns the NAND flash address cycle configuration
+*
+* @return 0 = Do not extra address cycle, 1 = Add extra cycle
+*
+*/
+/****************************************************************************/
+static inline int chipcHw_getNandExtraCycle(void);
+
+/****************************************************************************/
+/**
+* @brief Activates PIF interface
+*
+* This function activates PIF interface by taking control of LCD pins
+*
+* @note
+* When activated, LCD pins will be defined as follows for PIF operation
+*
+* CLD[17:0] = pif_data[17:0]
+* CLD[23:18] = pif_address[5:0]
+* CLPOWER = pif_wr_str
+* CLCP = pif_rd_str
+* CLAC = pif_hat1
+* CLFP = pif_hrdy1
+* CLLP = pif_hat2
+* GPIO[42] = pif_hrdy2
+*
+* In PIF mode, "pif_hrdy2" overrides other shared function for GPIO[42] pin
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_activatePifInterface(void);
+
+/****************************************************************************/
+/**
+* @brief Activates LCD interface
+*
+* This function activates LCD interface
+*
+* @note
+* When activated, LCD pins will be defined as follows
+*
+* CLD[17:0] = LCD data
+* CLD[23:18] = LCD data
+* CLPOWER = LCD power
+* CLCP =
+* CLAC = LCD ack
+* CLFP =
+* CLLP =
+*/
+/****************************************************************************/
+static inline void chipcHw_activateLcdInterface(void);
+
+/****************************************************************************/
+/**
+* @brief Deactivates PIF/LCD interface
+*
+* This function deactivates PIF/LCD interface
+*
+* @note
+* When deactivated LCD pins will be in rti-stated
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_deactivatePifLcdInterface(void);
+
+/****************************************************************************/
+/**
+* @brief Get to know the configuration of GPIO pin
+*
+*/
+/****************************************************************************/
+static inline chipcHw_GPIO_FUNCTION_e chipcHw_getGpioPinFunction(int pin /* GPIO Pin number */
+ );
+
+/****************************************************************************/
+/**
+* @brief Configure GPIO pin function
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setGpioPinFunction(int pin, /* GPIO Pin number */
+ chipcHw_GPIO_FUNCTION_e func /* Configuration function */
+ );
+
+/****************************************************************************/
+/**
+* @brief Set Pin slew rate
+*
+* This function sets the slew of individual pin
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setPinSlewRate(uint32_t pin, /* Pin of type chipcHw_PIN_XXXXX */
+ chipcHw_PIN_SLEW_RATE_e slewRate /* Pin slew rate */
+ );
+
+/****************************************************************************/
+/**
+* @brief Set Pin output drive current
+*
+* This function sets output drive current of individual pin
+*
+* Note: Avoid the use of the word 'current' since linux headers define this
+* to be the current task.
+*/
+/****************************************************************************/
+static inline void chipcHw_setPinOutputCurrent(uint32_t pin, /* Pin of type chipcHw_PIN_XXXXX */
+ chipcHw_PIN_CURRENT_STRENGTH_e curr /* Pin current rating */
+ );
+
+/****************************************************************************/
+/**
+* @brief Set Pin pullup register
+*
+* This function sets pullup register of individual pin
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setPinPullup(uint32_t pin, /* Pin of type chipcHw_PIN_XXXXX */
+ chipcHw_PIN_PULL_e pullup /* Pullup register settings */
+ );
+
+/****************************************************************************/
+/**
+* @brief Set Pin input type
+*
+* This function sets input type of individual Pin
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setPinInputType(uint32_t pin, /* Pin of type chipcHw_PIN_XXXXX */
+ chipcHw_PIN_INPUTTYPE_e inputType /* Pin input type */
+ );
+
+/****************************************************************************/
+/**
+* @brief Retrieves a string representation of the mux setting for a pin.
+*
+* @return Pointer to a character string.
+*/
+/****************************************************************************/
+
+const char *chipcHw_getGpioPinFunctionStr(int pin);
+
+/****************************************************************************/
+/** @brief issue warmReset
+ */
+/****************************************************************************/
+void chipcHw_reset(uint32_t mask);
+
+/****************************************************************************/
+/** @brief clock reconfigure
+ */
+/****************************************************************************/
+void chipcHw_clockReconfig(uint32_t busHz, uint32_t armRatio, uint32_t vpmRatio,
+ uint32_t ddrRatio);
+
+/****************************************************************************/
+/**
+* @brief Enable Spread Spectrum
+*
+* @note chipcHw_Init() must be called earlier
+*/
+/****************************************************************************/
+static inline void chipcHw_enableSpreadSpectrum(void);
+
+/****************************************************************************/
+/**
+* @brief Disable Spread Spectrum
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_disableSpreadSpectrum(void);
+
+/****************************************************************************/
+/** @brief Checks if software strap is enabled
+ *
+ * @return 1 : When enable
+ * 0 : When disable
+ */
+/****************************************************************************/
+static inline int chipcHw_isSoftwareStrapsEnable(void);
+
+/****************************************************************************/
+/** @brief Enable software strap
+ */
+/****************************************************************************/
+static inline void chipcHw_softwareStrapsEnable(void);
+
+/****************************************************************************/
+/** @brief Disable software strap
+ */
+/****************************************************************************/
+static inline void chipcHw_softwareStrapsDisable(void);
+
+/****************************************************************************/
+/** @brief PLL test enable
+ */
+/****************************************************************************/
+static inline void chipcHw_pllTestEnable(void);
+
+/****************************************************************************/
+/** @brief PLL2 test enable
+ */
+/****************************************************************************/
+static inline void chipcHw_pll2TestEnable(void);
+
+/****************************************************************************/
+/** @brief PLL test disable
+ */
+/****************************************************************************/
+static inline void chipcHw_pllTestDisable(void);
+
+/****************************************************************************/
+/** @brief PLL2 test disable
+ */
+/****************************************************************************/
+static inline void chipcHw_pll2TestDisable(void);
+
+/****************************************************************************/
+/** @brief Get PLL test status
+ */
+/****************************************************************************/
+static inline int chipcHw_isPllTestEnable(void);
+
+/****************************************************************************/
+/** @brief Get PLL2 test status
+ */
+/****************************************************************************/
+static inline int chipcHw_isPll2TestEnable(void);
+
+/****************************************************************************/
+/** @brief PLL test select
+ */
+/****************************************************************************/
+static inline void chipcHw_pllTestSelect(uint32_t val);
+
+/****************************************************************************/
+/** @brief PLL2 test select
+ */
+/****************************************************************************/
+static inline void chipcHw_pll2TestSelect(uint32_t val);
+
+/****************************************************************************/
+/** @brief Get PLL test selected option
+ */
+/****************************************************************************/
+static inline uint8_t chipcHw_getPllTestSelected(void);
+
+/****************************************************************************/
+/** @brief Get PLL2 test selected option
+ */
+/****************************************************************************/
+static inline uint8_t chipcHw_getPll2TestSelected(void);
+
+/****************************************************************************/
+/**
+* @brief Enables DDR SW phase alignment interrupt
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrPhaseAlignInterruptEnable(void);
+
+/****************************************************************************/
+/**
+* @brief Disables DDR SW phase alignment interrupt
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrPhaseAlignInterruptDisable(void);
+
+/****************************************************************************/
+/**
+* @brief Set VPM SW phase alignment interrupt mode
+*
+* This function sets VPM phase alignment interrupt
+*
+*/
+/****************************************************************************/
+static inline void
+chipcHw_vpmPhaseAlignInterruptMode(chipcHw_VPM_HW_PHASE_INTR_e mode);
+
+/****************************************************************************/
+/**
+* @brief Enable DDR phase alignment in software
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrSwPhaseAlignEnable(void);
+
+/****************************************************************************/
+/**
+* @brief Disable DDR phase alignment in software
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrSwPhaseAlignDisable(void);
+
+/****************************************************************************/
+/**
+* @brief Enable DDR phase alignment in hardware
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrHwPhaseAlignEnable(void);
+
+/****************************************************************************/
+/**
+* @brief Disable DDR phase alignment in hardware
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrHwPhaseAlignDisable(void);
+
+/****************************************************************************/
+/**
+* @brief Enable VPM phase alignment in software
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmSwPhaseAlignEnable(void);
+
+/****************************************************************************/
+/**
+* @brief Disable VPM phase alignment in software
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmSwPhaseAlignDisable(void);
+
+/****************************************************************************/
+/**
+* @brief Enable VPM phase alignment in hardware
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmHwPhaseAlignEnable(void);
+
+/****************************************************************************/
+/**
+* @brief Disable VPM phase alignment in hardware
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmHwPhaseAlignDisable(void);
+
+/****************************************************************************/
+/**
+* @brief Set DDR phase alignment margin in hardware
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setDdrHwPhaseAlignMargin(chipcHw_DDR_HW_PHASE_MARGIN_e margin /* Margin alinging DDR phase */
+ );
+
+/****************************************************************************/
+/**
+* @brief Set VPM phase alignment margin in hardware
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setVpmHwPhaseAlignMargin(chipcHw_VPM_HW_PHASE_MARGIN_e margin /* Margin alinging VPM phase */
+ );
+
+/****************************************************************************/
+/**
+* @brief Checks DDR phase aligned status done by HW
+*
+* @return 1: When aligned
+* 0: When not aligned
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_isDdrHwPhaseAligned(void);
+
+/****************************************************************************/
+/**
+* @brief Checks VPM phase aligned status done by HW
+*
+* @return 1: When aligned
+* 0: When not aligned
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_isVpmHwPhaseAligned(void);
+
+/****************************************************************************/
+/**
+* @brief Get DDR phase aligned status done by HW
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getDdrHwPhaseAlignStatus(void);
+
+/****************************************************************************/
+/**
+* @brief Get VPM phase aligned status done by HW
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getVpmHwPhaseAlignStatus(void);
+
+/****************************************************************************/
+/**
+* @brief Get DDR phase control value
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getDdrPhaseControl(void);
+
+/****************************************************************************/
+/**
+* @brief Get VPM phase control value
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getVpmPhaseControl(void);
+
+/****************************************************************************/
+/**
+* @brief DDR phase alignment timeout count
+*
+* @note If HW fails to perform the phase alignment, it will trigger
+* a DDR phase alignment timeout interrupt.
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrHwPhaseAlignTimeout(uint32_t busCycle /* Timeout in bus cycle */
+ );
+
+/****************************************************************************/
+/**
+* @brief VPM phase alignment timeout count
+*
+* @note If HW fails to perform the phase alignment, it will trigger
+* a VPM phase alignment timeout interrupt.
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmHwPhaseAlignTimeout(uint32_t busCycle /* Timeout in bus cycle */
+ );
+
+/****************************************************************************/
+/**
+* @brief DDR phase alignment timeout interrupt enable
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrHwPhaseAlignTimeoutInterruptEnable(void);
+
+/****************************************************************************/
+/**
+* @brief VPM phase alignment timeout interrupt enable
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmHwPhaseAlignTimeoutInterruptEnable(void);
+
+/****************************************************************************/
+/**
+* @brief DDR phase alignment timeout interrupt disable
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrHwPhaseAlignTimeoutInterruptDisable(void);
+
+/****************************************************************************/
+/**
+* @brief VPM phase alignment timeout interrupt disable
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmHwPhaseAlignTimeoutInterruptDisable(void);
+
+/****************************************************************************/
+/**
+* @brief Clear DDR phase alignment timeout interrupt
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrHwPhaseAlignTimeoutInterruptClear(void);
+
+/****************************************************************************/
+/**
+* @brief Clear VPM phase alignment timeout interrupt
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmHwPhaseAlignTimeoutInterruptClear(void);
+
+/* ---- Private Constants and Types -------------------------------------- */
+
+#endif /* CHIPC_DEF_H */
diff --git a/arch/arm/mach-bcmring/include/mach/csp/chipcHw_inline.h b/arch/arm/mach-bcmring/include/mach/csp/chipcHw_inline.h
new file mode 100644
index 00000000000..c78833acb37
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/chipcHw_inline.h
@@ -0,0 +1,1673 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#ifndef CHIPC_INLINE_H
+#define CHIPC_INLINE_H
+
+/* ---- Include Files ----------------------------------------------------- */
+
+#include <csp/errno.h>
+#include <csp/reg.h>
+#include <mach/csp/chipcHw_reg.h>
+#include <mach/csp/chipcHw_def.h>
+
+/* ---- Private Constants and Types --------------------------------------- */
+typedef enum {
+ chipcHw_OPTYPE_BYPASS, /* Bypass operation */
+ chipcHw_OPTYPE_OUTPUT /* Output operation */
+} chipcHw_OPTYPE_e;
+
+/* ---- Public Constants and Types ---------------------------------------- */
+/* ---- Public Variable Externs ------------------------------------------- */
+/* ---- Public Function Prototypes ---------------------------------------- */
+/* ---- Private Function Prototypes --------------------------------------- */
+static inline void chipcHw_setClock(chipcHw_CLOCK_e clock,
+ chipcHw_OPTYPE_e type, int mode);
+
+/****************************************************************************/
+/**
+* @brief Get Numeric Chip ID
+*
+* This function returns Chip ID that includes the revison number
+*
+* @return Complete numeric Chip ID
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getChipId(void)
+{
+ return pChipcHw->ChipId;
+}
+
+/****************************************************************************/
+/**
+* @brief Enable Spread Spectrum
+*
+* @note chipcHw_Init() must be called earlier
+*/
+/****************************************************************************/
+static inline void chipcHw_enableSpreadSpectrum(void)
+{
+ if ((pChipcHw->
+ PLLPreDivider & chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MASK) !=
+ chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_INTEGER) {
+ ddrcReg_PHY_ADDR_CTL_REGP->ssCfg =
+ (0xFFFF << ddrcReg_PHY_ADDR_SS_CFG_NDIV_AMPLITUDE_SHIFT) |
+ (ddrcReg_PHY_ADDR_SS_CFG_MIN_CYCLE_PER_TICK <<
+ ddrcReg_PHY_ADDR_SS_CFG_CYCLE_PER_TICK_SHIFT);
+ ddrcReg_PHY_ADDR_CTL_REGP->ssCtl |=
+ ddrcReg_PHY_ADDR_SS_CTRL_ENABLE;
+ }
+}
+
+/****************************************************************************/
+/**
+* @brief Disable Spread Spectrum
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_disableSpreadSpectrum(void)
+{
+ ddrcReg_PHY_ADDR_CTL_REGP->ssCtl &= ~ddrcReg_PHY_ADDR_SS_CTRL_ENABLE;
+}
+
+/****************************************************************************/
+/**
+* @brief Get Chip Product ID
+*
+* This function returns Chip Product ID
+*
+* @return Chip Product ID
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getChipProductId(void)
+{
+ return (pChipcHw->
+ ChipId & chipcHw_REG_CHIPID_BASE_MASK) >>
+ chipcHw_REG_CHIPID_BASE_SHIFT;
+}
+
+/****************************************************************************/
+/**
+* @brief Get revision number
+*
+* This function returns revision number of the chip
+*
+* @return Revision number
+*/
+/****************************************************************************/
+static inline chipcHw_REV_NUMBER_e chipcHw_getChipRevisionNumber(void)
+{
+ return pChipcHw->ChipId & chipcHw_REG_CHIPID_REV_MASK;
+}
+
+/****************************************************************************/
+/**
+* @brief Enables bus interface clock
+*
+* Enables bus interface clock of various device
+*
+* @return void
+*
+* @note use chipcHw_REG_BUS_CLOCK_XXXX for mask
+*/
+/****************************************************************************/
+static inline void chipcHw_busInterfaceClockEnable(uint32_t mask)
+{
+ reg32_modify_or(&pChipcHw->BusIntfClock, mask);
+}
+
+/****************************************************************************/
+/**
+* @brief Disables bus interface clock
+*
+* Disables bus interface clock of various device
+*
+* @return void
+*
+* @note use chipcHw_REG_BUS_CLOCK_XXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_busInterfaceClockDisable(uint32_t mask)
+{
+ reg32_modify_and(&pChipcHw->BusIntfClock, ~mask);
+}
+
+/****************************************************************************/
+/**
+* @brief Get status (enabled/disabled) of bus interface clock
+*
+* This function returns the status of devices' bus interface clock
+*
+* @return Bus interface clock
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getBusInterfaceClockStatus(void)
+{
+ return pChipcHw->BusIntfClock;
+}
+
+/****************************************************************************/
+/**
+* @brief Enables various audio channels
+*
+* Enables audio channel
+*
+* @return void
+*
+* @note use chipcHw_REG_AUDIO_CHANNEL_XXXXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_audioChannelEnable(uint32_t mask)
+{
+ reg32_modify_or(&pChipcHw->AudioEnable, mask);
+}
+
+/****************************************************************************/
+/**
+* @brief Disables various audio channels
+*
+* Disables audio channel
+*
+* @return void
+*
+* @note use chipcHw_REG_AUDIO_CHANNEL_XXXXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_audioChannelDisable(uint32_t mask)
+{
+ reg32_modify_and(&pChipcHw->AudioEnable, ~mask);
+}
+
+/****************************************************************************/
+/**
+* @brief Soft resets devices
+*
+* Soft resets various devices
+*
+* @return void
+*
+* @note use chipcHw_REG_SOFT_RESET_XXXXXX defines
+*/
+/****************************************************************************/
+static inline void chipcHw_softReset(uint64_t mask)
+{
+ chipcHw_softResetEnable(mask);
+ chipcHw_softResetDisable(mask);
+}
+
+static inline void chipcHw_softResetDisable(uint64_t mask)
+{
+ uint32_t ctrl1 = (uint32_t) mask;
+ uint32_t ctrl2 = (uint32_t) (mask >> 32);
+
+ /* Deassert module soft reset */
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->SoftReset1 ^= ctrl1;
+ pChipcHw->SoftReset2 ^= (ctrl2 & (~chipcHw_REG_SOFT_RESET_UNHOLD_MASK));
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+static inline void chipcHw_softResetEnable(uint64_t mask)
+{
+ uint32_t ctrl1 = (uint32_t) mask;
+ uint32_t ctrl2 = (uint32_t) (mask >> 32);
+ uint32_t unhold = 0;
+
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->SoftReset1 |= ctrl1;
+ /* Mask out unhold request bits */
+ pChipcHw->SoftReset2 |= (ctrl2 & (~chipcHw_REG_SOFT_RESET_UNHOLD_MASK));
+
+ /* Process unhold requests */
+ if (ctrl2 & chipcHw_REG_SOFT_RESET_VPM_GLOBAL_UNHOLD) {
+ unhold = chipcHw_REG_SOFT_RESET_VPM_GLOBAL_HOLD;
+ }
+
+ if (ctrl2 & chipcHw_REG_SOFT_RESET_VPM_UNHOLD) {
+ unhold |= chipcHw_REG_SOFT_RESET_VPM_HOLD;
+ }
+
+ if (ctrl2 & chipcHw_REG_SOFT_RESET_ARM_UNHOLD) {
+ unhold |= chipcHw_REG_SOFT_RESET_ARM_HOLD;
+ }
+
+ if (unhold) {
+ /* Make sure unhold request is effective */
+ pChipcHw->SoftReset1 &= ~unhold;
+ }
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Configures misc CHIP functionality
+*
+* Configures CHIP functionality
+*
+* @return void
+*
+* @note use chipcHw_REG_MISC_CTRL_XXXXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_miscControl(uint32_t mask)
+{
+ reg32_write(&pChipcHw->MiscCtrl, mask);
+}
+
+static inline void chipcHw_miscControlDisable(uint32_t mask)
+{
+ reg32_modify_and(&pChipcHw->MiscCtrl, ~mask);
+}
+
+static inline void chipcHw_miscControlEnable(uint32_t mask)
+{
+ reg32_modify_or(&pChipcHw->MiscCtrl, mask);
+}
+
+/****************************************************************************/
+/**
+* @brief Set OTP options
+*
+* Set OTP options
+*
+* @return void
+*
+* @note use chipcHw_REG_OTP_XXXXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_setOTPOption(uint64_t mask)
+{
+ uint32_t ctrl1 = (uint32_t) mask;
+ uint32_t ctrl2 = (uint32_t) (mask >> 32);
+
+ reg32_modify_or(&pChipcHw->SoftOTP1, ctrl1);
+ reg32_modify_or(&pChipcHw->SoftOTP2, ctrl2);
+}
+
+/****************************************************************************/
+/**
+* @brief Get sticky bits
+*
+* @return Sticky bit options of type chipcHw_REG_STICKY_XXXXXX
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getStickyBits(void)
+{
+ return pChipcHw->Sticky;
+}
+
+/****************************************************************************/
+/**
+* @brief Set sticky bits
+*
+* @return void
+*
+* @note use chipcHw_REG_STICKY_XXXXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_setStickyBits(uint32_t mask)
+{
+ uint32_t bits = 0;
+
+ REG_LOCAL_IRQ_SAVE;
+ if (mask & chipcHw_REG_STICKY_POR_BROM) {
+ bits |= chipcHw_REG_STICKY_POR_BROM;
+ } else {
+ uint32_t sticky;
+ sticky = pChipcHw->Sticky;
+
+ if ((mask & chipcHw_REG_STICKY_BOOT_DONE)
+ && (sticky & chipcHw_REG_STICKY_BOOT_DONE) == 0) {
+ bits |= chipcHw_REG_STICKY_BOOT_DONE;
+ }
+ if ((mask & chipcHw_REG_STICKY_GENERAL_1)
+ && (sticky & chipcHw_REG_STICKY_GENERAL_1) == 0) {
+ bits |= chipcHw_REG_STICKY_GENERAL_1;
+ }
+ if ((mask & chipcHw_REG_STICKY_GENERAL_2)
+ && (sticky & chipcHw_REG_STICKY_GENERAL_2) == 0) {
+ bits |= chipcHw_REG_STICKY_GENERAL_2;
+ }
+ if ((mask & chipcHw_REG_STICKY_GENERAL_3)
+ && (sticky & chipcHw_REG_STICKY_GENERAL_3) == 0) {
+ bits |= chipcHw_REG_STICKY_GENERAL_3;
+ }
+ if ((mask & chipcHw_REG_STICKY_GENERAL_4)
+ && (sticky & chipcHw_REG_STICKY_GENERAL_4) == 0) {
+ bits |= chipcHw_REG_STICKY_GENERAL_4;
+ }
+ if ((mask & chipcHw_REG_STICKY_GENERAL_5)
+ && (sticky & chipcHw_REG_STICKY_GENERAL_5) == 0) {
+ bits |= chipcHw_REG_STICKY_GENERAL_5;
+ }
+ }
+ pChipcHw->Sticky = bits;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Clear sticky bits
+*
+* @return void
+*
+* @note use chipcHw_REG_STICKY_XXXXXX
+*/
+/****************************************************************************/
+static inline void chipcHw_clearStickyBits(uint32_t mask)
+{
+ uint32_t bits = 0;
+
+ REG_LOCAL_IRQ_SAVE;
+ if (mask &
+ (chipcHw_REG_STICKY_BOOT_DONE | chipcHw_REG_STICKY_GENERAL_1 |
+ chipcHw_REG_STICKY_GENERAL_2 | chipcHw_REG_STICKY_GENERAL_3 |
+ chipcHw_REG_STICKY_GENERAL_4 | chipcHw_REG_STICKY_GENERAL_5)) {
+ uint32_t sticky = pChipcHw->Sticky;
+
+ if ((mask & chipcHw_REG_STICKY_BOOT_DONE)
+ && (sticky & chipcHw_REG_STICKY_BOOT_DONE)) {
+ bits = chipcHw_REG_STICKY_BOOT_DONE;
+ mask &= ~chipcHw_REG_STICKY_BOOT_DONE;
+ }
+ if ((mask & chipcHw_REG_STICKY_GENERAL_1)
+ && (sticky & chipcHw_REG_STICKY_GENERAL_1)) {
+ bits |= chipcHw_REG_STICKY_GENERAL_1;
+ mask &= ~chipcHw_REG_STICKY_GENERAL_1;
+ }
+ if ((mask & chipcHw_REG_STICKY_GENERAL_2)
+ && (sticky & chipcHw_REG_STICKY_GENERAL_2)) {
+ bits |= chipcHw_REG_STICKY_GENERAL_2;
+ mask &= ~chipcHw_REG_STICKY_GENERAL_2;
+ }
+ if ((mask & chipcHw_REG_STICKY_GENERAL_3)
+ && (sticky & chipcHw_REG_STICKY_GENERAL_3)) {
+ bits |= chipcHw_REG_STICKY_GENERAL_3;
+ mask &= ~chipcHw_REG_STICKY_GENERAL_3;
+ }
+ if ((mask & chipcHw_REG_STICKY_GENERAL_4)
+ && (sticky & chipcHw_REG_STICKY_GENERAL_4)) {
+ bits |= chipcHw_REG_STICKY_GENERAL_4;
+ mask &= ~chipcHw_REG_STICKY_GENERAL_4;
+ }
+ if ((mask & chipcHw_REG_STICKY_GENERAL_5)
+ && (sticky & chipcHw_REG_STICKY_GENERAL_5)) {
+ bits |= chipcHw_REG_STICKY_GENERAL_5;
+ mask &= ~chipcHw_REG_STICKY_GENERAL_5;
+ }
+ }
+ pChipcHw->Sticky = bits | mask;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Get software strap value
+*
+* Retrieves software strap value
+*
+* @return Software strap value
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getSoftStraps(void)
+{
+ return pChipcHw->SoftStraps;
+}
+
+/****************************************************************************/
+/**
+* @brief Set software override strap options
+*
+* set software override strap options
+*
+* @return nothing
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setSoftStraps(uint32_t strapOptions)
+{
+ reg32_write(&pChipcHw->SoftStraps, strapOptions);
+}
+
+/****************************************************************************/
+/**
+* @brief Get Pin Strap Options
+*
+* This function returns the raw boot strap options
+*
+* @return strap options
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getPinStraps(void)
+{
+ return pChipcHw->PinStraps;
+}
+
+/****************************************************************************/
+/**
+* @brief Get Valid Strap Options
+*
+* This function returns the valid raw boot strap options
+*
+* @return strap options
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getValidStraps(void)
+{
+ uint32_t softStraps;
+
+ /*
+ ** Always return the SoftStraps - bootROM calls chipcHw_initValidStraps
+ ** which copies HW straps to soft straps if there is no override
+ */
+ softStraps = chipcHw_getSoftStraps();
+
+ return softStraps;
+}
+
+/****************************************************************************/
+/**
+* @brief Initialize valid pin strap options
+*
+* Retrieves valid pin strap options by copying HW strap options to soft register
+* (if chipcHw_STRAPS_SOFT_OVERRIDE not set)
+*
+* @return nothing
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_initValidStraps(void)
+{
+ uint32_t softStraps;
+
+ REG_LOCAL_IRQ_SAVE;
+ softStraps = chipcHw_getSoftStraps();
+
+ if ((softStraps & chipcHw_STRAPS_SOFT_OVERRIDE) == 0) {
+ /* Copy HW straps to software straps */
+ chipcHw_setSoftStraps(chipcHw_getPinStraps());
+ }
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Get boot device
+*
+* This function returns the device type used in booting the system
+*
+* @return Boot device of type chipcHw_BOOT_DEVICE
+*
+*/
+/****************************************************************************/
+static inline chipcHw_BOOT_DEVICE_e chipcHw_getBootDevice(void)
+{
+ return chipcHw_getValidStraps() & chipcHw_STRAPS_BOOT_DEVICE_MASK;
+}
+
+/****************************************************************************/
+/**
+* @brief Get boot mode
+*
+* This function returns the way the system was booted
+*
+* @return Boot mode of type chipcHw_BOOT_MODE
+*
+*/
+/****************************************************************************/
+static inline chipcHw_BOOT_MODE_e chipcHw_getBootMode(void)
+{
+ return chipcHw_getValidStraps() & chipcHw_STRAPS_BOOT_MODE_MASK;
+}
+
+/****************************************************************************/
+/**
+* @brief Get NAND flash page size
+*
+* This function returns the NAND device page size
+*
+* @return Boot NAND device page size
+*
+*/
+/****************************************************************************/
+static inline chipcHw_NAND_PAGESIZE_e chipcHw_getNandPageSize(void)
+{
+ return chipcHw_getValidStraps() & chipcHw_STRAPS_NAND_PAGESIZE_MASK;
+}
+
+/****************************************************************************/
+/**
+* @brief Get NAND flash address cycle configuration
+*
+* This function returns the NAND flash address cycle configuration
+*
+* @return 0 = Do not extra address cycle, 1 = Add extra cycle
+*
+*/
+/****************************************************************************/
+static inline int chipcHw_getNandExtraCycle(void)
+{
+ if (chipcHw_getValidStraps() & chipcHw_STRAPS_NAND_EXTRA_CYCLE) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/****************************************************************************/
+/**
+* @brief Activates PIF interface
+*
+* This function activates PIF interface by taking control of LCD pins
+*
+* @note
+* When activated, LCD pins will be defined as follows for PIF operation
+*
+* CLD[17:0] = pif_data[17:0]
+* CLD[23:18] = pif_address[5:0]
+* CLPOWER = pif_wr_str
+* CLCP = pif_rd_str
+* CLAC = pif_hat1
+* CLFP = pif_hrdy1
+* CLLP = pif_hat2
+* GPIO[42] = pif_hrdy2
+*
+* In PIF mode, "pif_hrdy2" overrides other shared function for GPIO[42] pin
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_activatePifInterface(void)
+{
+ reg32_write(&pChipcHw->LcdPifMode, chipcHw_REG_PIF_PIN_ENABLE);
+}
+
+/****************************************************************************/
+/**
+* @brief Activates LCD interface
+*
+* This function activates LCD interface
+*
+* @note
+* When activated, LCD pins will be defined as follows
+*
+* CLD[17:0] = LCD data
+* CLD[23:18] = LCD data
+* CLPOWER = LCD power
+* CLCP =
+* CLAC = LCD ack
+* CLFP =
+* CLLP =
+*/
+/****************************************************************************/
+static inline void chipcHw_activateLcdInterface(void)
+{
+ reg32_write(&pChipcHw->LcdPifMode, chipcHw_REG_LCD_PIN_ENABLE);
+}
+
+/****************************************************************************/
+/**
+* @brief Deactivates PIF/LCD interface
+*
+* This function deactivates PIF/LCD interface
+*
+* @note
+* When deactivated LCD pins will be in rti-stated
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_deactivatePifLcdInterface(void)
+{
+ reg32_write(&pChipcHw->LcdPifMode, 0);
+}
+
+/****************************************************************************/
+/**
+* @brief Select GE2
+*
+* This function select GE2 as the graphic engine
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_selectGE2(void)
+{
+ reg32_modify_and(&pChipcHw->MiscCtrl, ~chipcHw_REG_MISC_CTRL_GE_SEL);
+}
+
+/****************************************************************************/
+/**
+* @brief Select GE3
+*
+* This function select GE3 as the graphic engine
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_selectGE3(void)
+{
+ reg32_modify_or(&pChipcHw->MiscCtrl, chipcHw_REG_MISC_CTRL_GE_SEL);
+}
+
+/****************************************************************************/
+/**
+* @brief Get to know the configuration of GPIO pin
+*
+*/
+/****************************************************************************/
+static inline chipcHw_GPIO_FUNCTION_e chipcHw_getGpioPinFunction(int pin)
+{
+ return (*((uint32_t *) chipcHw_REG_GPIO_MUX(pin)) &
+ (chipcHw_REG_GPIO_MUX_MASK <<
+ chipcHw_REG_GPIO_MUX_POSITION(pin))) >>
+ chipcHw_REG_GPIO_MUX_POSITION(pin);
+}
+
+/****************************************************************************/
+/**
+* @brief Configure GPIO pin function
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setGpioPinFunction(int pin,
+ chipcHw_GPIO_FUNCTION_e func)
+{
+ REG_LOCAL_IRQ_SAVE;
+ *((uint32_t *) chipcHw_REG_GPIO_MUX(pin)) &=
+ ~(chipcHw_REG_GPIO_MUX_MASK << chipcHw_REG_GPIO_MUX_POSITION(pin));
+ *((uint32_t *) chipcHw_REG_GPIO_MUX(pin)) |=
+ func << chipcHw_REG_GPIO_MUX_POSITION(pin);
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Set Pin slew rate
+*
+* This function sets the slew of individual pin
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setPinSlewRate(uint32_t pin,
+ chipcHw_PIN_SLEW_RATE_e slewRate)
+{
+ REG_LOCAL_IRQ_SAVE;
+ *((uint32_t *) chipcHw_REG_SLEW_RATE(pin)) &=
+ ~(chipcHw_REG_SLEW_RATE_MASK <<
+ chipcHw_REG_SLEW_RATE_POSITION(pin));
+ *((uint32_t *) chipcHw_REG_SLEW_RATE(pin)) |=
+ (uint32_t) slewRate << chipcHw_REG_SLEW_RATE_POSITION(pin);
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Set Pin output drive current
+*
+* This function sets output drive current of individual pin
+*
+* Note: Avoid the use of the word 'current' since linux headers define this
+* to be the current task.
+*/
+/****************************************************************************/
+static inline void chipcHw_setPinOutputCurrent(uint32_t pin,
+ chipcHw_PIN_CURRENT_STRENGTH_e
+ curr)
+{
+ REG_LOCAL_IRQ_SAVE;
+ *((uint32_t *) chipcHw_REG_CURRENT(pin)) &=
+ ~(chipcHw_REG_CURRENT_MASK << chipcHw_REG_CURRENT_POSITION(pin));
+ *((uint32_t *) chipcHw_REG_CURRENT(pin)) |=
+ (uint32_t) curr << chipcHw_REG_CURRENT_POSITION(pin);
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Set Pin pullup register
+*
+* This function sets pullup register of individual pin
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setPinPullup(uint32_t pin, chipcHw_PIN_PULL_e pullup)
+{
+ REG_LOCAL_IRQ_SAVE;
+ *((uint32_t *) chipcHw_REG_PULLUP(pin)) &=
+ ~(chipcHw_REG_PULLUP_MASK << chipcHw_REG_PULLUP_POSITION(pin));
+ *((uint32_t *) chipcHw_REG_PULLUP(pin)) |=
+ (uint32_t) pullup << chipcHw_REG_PULLUP_POSITION(pin);
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Set Pin input type
+*
+* This function sets input type of individual pin
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setPinInputType(uint32_t pin,
+ chipcHw_PIN_INPUTTYPE_e inputType)
+{
+ REG_LOCAL_IRQ_SAVE;
+ *((uint32_t *) chipcHw_REG_INPUTTYPE(pin)) &=
+ ~(chipcHw_REG_INPUTTYPE_MASK <<
+ chipcHw_REG_INPUTTYPE_POSITION(pin));
+ *((uint32_t *) chipcHw_REG_INPUTTYPE(pin)) |=
+ (uint32_t) inputType << chipcHw_REG_INPUTTYPE_POSITION(pin);
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Power up the USB PHY
+*
+* This function powers up the USB PHY
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_powerUpUsbPhy(void)
+{
+ reg32_modify_and(&pChipcHw->MiscCtrl,
+ chipcHw_REG_MISC_CTRL_USB_POWERON);
+}
+
+/****************************************************************************/
+/**
+* @brief Power down the USB PHY
+*
+* This function powers down the USB PHY
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_powerDownUsbPhy(void)
+{
+ reg32_modify_or(&pChipcHw->MiscCtrl,
+ chipcHw_REG_MISC_CTRL_USB_POWEROFF);
+}
+
+/****************************************************************************/
+/**
+* @brief Set the 2nd USB as host
+*
+* This function sets the 2nd USB as host
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setUsbHost(void)
+{
+ reg32_modify_or(&pChipcHw->MiscCtrl,
+ chipcHw_REG_MISC_CTRL_USB_MODE_HOST);
+}
+
+/****************************************************************************/
+/**
+* @brief Set the 2nd USB as device
+*
+* This function sets the 2nd USB as device
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setUsbDevice(void)
+{
+ reg32_modify_and(&pChipcHw->MiscCtrl,
+ chipcHw_REG_MISC_CTRL_USB_MODE_DEVICE);
+}
+
+/****************************************************************************/
+/**
+* @brief Lower layer funtion to enable/disable a clock of a certain device
+*
+* This function enables/disables a core clock
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_setClock(chipcHw_CLOCK_e clock,
+ chipcHw_OPTYPE_e type, int mode)
+{
+ volatile uint32_t *pPLLReg = (uint32_t *) 0x0;
+ volatile uint32_t *pClockCtrl = (uint32_t *) 0x0;
+
+ switch (clock) {
+ case chipcHw_CLOCK_DDR:
+ pPLLReg = &pChipcHw->DDRClock;
+ break;
+ case chipcHw_CLOCK_ARM:
+ pPLLReg = &pChipcHw->ARMClock;
+ break;
+ case chipcHw_CLOCK_ESW:
+ pPLLReg = &pChipcHw->ESWClock;
+ break;
+ case chipcHw_CLOCK_VPM:
+ pPLLReg = &pChipcHw->VPMClock;
+ break;
+ case chipcHw_CLOCK_ESW125:
+ pPLLReg = &pChipcHw->ESW125Clock;
+ break;
+ case chipcHw_CLOCK_UART:
+ pPLLReg = &pChipcHw->UARTClock;
+ break;
+ case chipcHw_CLOCK_SDIO0:
+ pPLLReg = &pChipcHw->SDIO0Clock;
+ break;
+ case chipcHw_CLOCK_SDIO1:
+ pPLLReg = &pChipcHw->SDIO1Clock;
+ break;
+ case chipcHw_CLOCK_SPI:
+ pPLLReg = &pChipcHw->SPIClock;
+ break;
+ case chipcHw_CLOCK_ETM:
+ pPLLReg = &pChipcHw->ETMClock;
+ break;
+ case chipcHw_CLOCK_USB:
+ pPLLReg = &pChipcHw->USBClock;
+ if (type == chipcHw_OPTYPE_OUTPUT) {
+ if (mode) {
+ reg32_modify_and(pPLLReg,
+ ~chipcHw_REG_PLL_CLOCK_POWER_DOWN);
+ } else {
+ reg32_modify_or(pPLLReg,
+ chipcHw_REG_PLL_CLOCK_POWER_DOWN);
+ }
+ }
+ break;
+ case chipcHw_CLOCK_LCD:
+ pPLLReg = &pChipcHw->LCDClock;
+ if (type == chipcHw_OPTYPE_OUTPUT) {
+ if (mode) {
+ reg32_modify_and(pPLLReg,
+ ~chipcHw_REG_PLL_CLOCK_POWER_DOWN);
+ } else {
+ reg32_modify_or(pPLLReg,
+ chipcHw_REG_PLL_CLOCK_POWER_DOWN);
+ }
+ }
+ break;
+ case chipcHw_CLOCK_APM:
+ pPLLReg = &pChipcHw->APMClock;
+ if (type == chipcHw_OPTYPE_OUTPUT) {
+ if (mode) {
+ reg32_modify_and(pPLLReg,
+ ~chipcHw_REG_PLL_CLOCK_POWER_DOWN);
+ } else {
+ reg32_modify_or(pPLLReg,
+ chipcHw_REG_PLL_CLOCK_POWER_DOWN);
+ }
+ }
+ break;
+ case chipcHw_CLOCK_BUS:
+ pClockCtrl = &pChipcHw->ACLKClock;
+ break;
+ case chipcHw_CLOCK_OTP:
+ pClockCtrl = &pChipcHw->OTPClock;
+ break;
+ case chipcHw_CLOCK_I2C:
+ pClockCtrl = &pChipcHw->I2CClock;
+ break;
+ case chipcHw_CLOCK_I2S0:
+ pClockCtrl = &pChipcHw->I2S0Clock;
+ break;
+ case chipcHw_CLOCK_RTBUS:
+ pClockCtrl = &pChipcHw->RTBUSClock;
+ break;
+ case chipcHw_CLOCK_APM100:
+ pClockCtrl = &pChipcHw->APM100Clock;
+ break;
+ case chipcHw_CLOCK_TSC:
+ pClockCtrl = &pChipcHw->TSCClock;
+ break;
+ case chipcHw_CLOCK_LED:
+ pClockCtrl = &pChipcHw->LEDClock;
+ break;
+ case chipcHw_CLOCK_I2S1:
+ pClockCtrl = &pChipcHw->I2S1Clock;
+ break;
+ }
+
+ if (pPLLReg) {
+ switch (type) {
+ case chipcHw_OPTYPE_OUTPUT:
+ /* PLL clock output enable/disable */
+ if (mode) {
+ if (clock == chipcHw_CLOCK_DDR) {
+ /* DDR clock enable is inverted */
+ reg32_modify_and(pPLLReg,
+ ~chipcHw_REG_PLL_CLOCK_OUTPUT_ENABLE);
+ } else {
+ reg32_modify_or(pPLLReg,
+ chipcHw_REG_PLL_CLOCK_OUTPUT_ENABLE);
+ }
+ } else {
+ if (clock == chipcHw_CLOCK_DDR) {
+ /* DDR clock disable is inverted */
+ reg32_modify_or(pPLLReg,
+ chipcHw_REG_PLL_CLOCK_OUTPUT_ENABLE);
+ } else {
+ reg32_modify_and(pPLLReg,
+ ~chipcHw_REG_PLL_CLOCK_OUTPUT_ENABLE);
+ }
+ }
+ break;
+ case chipcHw_OPTYPE_BYPASS:
+ /* PLL clock bypass enable/disable */
+ if (mode) {
+ reg32_modify_or(pPLLReg,
+ chipcHw_REG_PLL_CLOCK_BYPASS_SELECT);
+ } else {
+ reg32_modify_and(pPLLReg,
+ ~chipcHw_REG_PLL_CLOCK_BYPASS_SELECT);
+ }
+ break;
+ }
+ } else if (pClockCtrl) {
+ switch (type) {
+ case chipcHw_OPTYPE_OUTPUT:
+ if (mode) {
+ reg32_modify_or(pClockCtrl,
+ chipcHw_REG_DIV_CLOCK_OUTPUT_ENABLE);
+ } else {
+ reg32_modify_and(pClockCtrl,
+ ~chipcHw_REG_DIV_CLOCK_OUTPUT_ENABLE);
+ }
+ break;
+ case chipcHw_OPTYPE_BYPASS:
+ if (mode) {
+ reg32_modify_or(pClockCtrl,
+ chipcHw_REG_DIV_CLOCK_BYPASS_SELECT);
+ } else {
+ reg32_modify_and(pClockCtrl,
+ ~chipcHw_REG_DIV_CLOCK_BYPASS_SELECT);
+ }
+ break;
+ }
+ }
+}
+
+/****************************************************************************/
+/**
+* @brief Disables a core clock of a certain device
+*
+* This function disables a core clock
+*
+* @note no change in power consumption
+*/
+/****************************************************************************/
+static inline void chipcHw_setClockDisable(chipcHw_CLOCK_e clock)
+{
+
+ /* Disable output of the clock */
+ chipcHw_setClock(clock, chipcHw_OPTYPE_OUTPUT, 0);
+}
+
+/****************************************************************************/
+/**
+* @brief Enable a core clock of a certain device
+*
+* This function enables a core clock
+*
+* @note no change in power consumption
+*/
+/****************************************************************************/
+static inline void chipcHw_setClockEnable(chipcHw_CLOCK_e clock)
+{
+
+ /* Enable output of the clock */
+ chipcHw_setClock(clock, chipcHw_OPTYPE_OUTPUT, 1);
+}
+
+/****************************************************************************/
+/**
+* @brief Enables bypass clock of a certain device
+*
+* This function enables bypass clock
+*
+* @note Doesnot affect the bus interface clock
+*/
+/****************************************************************************/
+static inline void chipcHw_bypassClockEnable(chipcHw_CLOCK_e clock)
+{
+ /* Enable bypass clock */
+ chipcHw_setClock(clock, chipcHw_OPTYPE_BYPASS, 1);
+}
+
+/****************************************************************************/
+/**
+* @brief Disabled bypass clock of a certain device
+*
+* This function disables bypass clock
+*
+* @note Doesnot affect the bus interface clock
+*/
+/****************************************************************************/
+static inline void chipcHw_bypassClockDisable(chipcHw_CLOCK_e clock)
+{
+ /* Disable bypass clock */
+ chipcHw_setClock(clock, chipcHw_OPTYPE_BYPASS, 0);
+
+}
+
+/****************************************************************************/
+/** @brief Checks if software strap is enabled
+ *
+ * @return 1 : When enable
+ * 0 : When disable
+ */
+/****************************************************************************/
+static inline int chipcHw_isSoftwareStrapsEnable(void)
+{
+ return pChipcHw->SoftStraps & 0x00000001;
+}
+
+/****************************************************************************/
+/** @brief Enable software strap
+ */
+/****************************************************************************/
+static inline void chipcHw_softwareStrapsEnable(void)
+{
+ reg32_modify_or(&pChipcHw->SoftStraps, 0x00000001);
+}
+
+/****************************************************************************/
+/** @brief Disable software strap
+ */
+/****************************************************************************/
+static inline void chipcHw_softwareStrapsDisable(void)
+{
+ reg32_modify_and(&pChipcHw->SoftStraps, (~0x00000001));
+}
+
+/****************************************************************************/
+/** @brief PLL test enable
+ */
+/****************************************************************************/
+static inline void chipcHw_pllTestEnable(void)
+{
+ reg32_modify_or(&pChipcHw->PLLConfig,
+ chipcHw_REG_PLL_CONFIG_TEST_ENABLE);
+}
+
+/****************************************************************************/
+/** @brief PLL2 test enable
+ */
+/****************************************************************************/
+static inline void chipcHw_pll2TestEnable(void)
+{
+ reg32_modify_or(&pChipcHw->PLLConfig2,
+ chipcHw_REG_PLL_CONFIG_TEST_ENABLE);
+}
+
+/****************************************************************************/
+/** @brief PLL test disable
+ */
+/****************************************************************************/
+static inline void chipcHw_pllTestDisable(void)
+{
+ reg32_modify_and(&pChipcHw->PLLConfig,
+ ~chipcHw_REG_PLL_CONFIG_TEST_ENABLE);
+}
+
+/****************************************************************************/
+/** @brief PLL2 test disable
+ */
+/****************************************************************************/
+static inline void chipcHw_pll2TestDisable(void)
+{
+ reg32_modify_and(&pChipcHw->PLLConfig2,
+ ~chipcHw_REG_PLL_CONFIG_TEST_ENABLE);
+}
+
+/****************************************************************************/
+/** @brief Get PLL test status
+ */
+/****************************************************************************/
+static inline int chipcHw_isPllTestEnable(void)
+{
+ return pChipcHw->PLLConfig & chipcHw_REG_PLL_CONFIG_TEST_ENABLE;
+}
+
+/****************************************************************************/
+/** @brief Get PLL2 test status
+ */
+/****************************************************************************/
+static inline int chipcHw_isPll2TestEnable(void)
+{
+ return pChipcHw->PLLConfig2 & chipcHw_REG_PLL_CONFIG_TEST_ENABLE;
+}
+
+/****************************************************************************/
+/** @brief PLL test select
+ */
+/****************************************************************************/
+static inline void chipcHw_pllTestSelect(uint32_t val)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->PLLConfig &= ~chipcHw_REG_PLL_CONFIG_TEST_SELECT_MASK;
+ pChipcHw->PLLConfig |=
+ (val) << chipcHw_REG_PLL_CONFIG_TEST_SELECT_SHIFT;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/** @brief PLL2 test select
+ */
+/****************************************************************************/
+static inline void chipcHw_pll2TestSelect(uint32_t val)
+{
+
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->PLLConfig2 &= ~chipcHw_REG_PLL_CONFIG_TEST_SELECT_MASK;
+ pChipcHw->PLLConfig2 |=
+ (val) << chipcHw_REG_PLL_CONFIG_TEST_SELECT_SHIFT;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/** @brief Get PLL test selected option
+ */
+/****************************************************************************/
+static inline uint8_t chipcHw_getPllTestSelected(void)
+{
+ return (uint8_t) ((pChipcHw->
+ PLLConfig & chipcHw_REG_PLL_CONFIG_TEST_SELECT_MASK)
+ >> chipcHw_REG_PLL_CONFIG_TEST_SELECT_SHIFT);
+}
+
+/****************************************************************************/
+/** @brief Get PLL2 test selected option
+ */
+/****************************************************************************/
+static inline uint8_t chipcHw_getPll2TestSelected(void)
+{
+ return (uint8_t) ((pChipcHw->
+ PLLConfig2 & chipcHw_REG_PLL_CONFIG_TEST_SELECT_MASK)
+ >> chipcHw_REG_PLL_CONFIG_TEST_SELECT_SHIFT);
+}
+
+/****************************************************************************/
+/**
+* @brief Disable the PLL1
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_pll1Disable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->PLLConfig |= chipcHw_REG_PLL_CONFIG_POWER_DOWN;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Disable the PLL2
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_pll2Disable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->PLLConfig2 |= chipcHw_REG_PLL_CONFIG_POWER_DOWN;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Enables DDR SW phase alignment interrupt
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrPhaseAlignInterruptEnable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->Spare1 |= chipcHw_REG_SPARE1_DDR_PHASE_INTR_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Disables DDR SW phase alignment interrupt
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrPhaseAlignInterruptDisable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->Spare1 &= ~chipcHw_REG_SPARE1_DDR_PHASE_INTR_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Set VPM SW phase alignment interrupt mode
+*
+* This function sets VPM phase alignment interrupt
+*/
+/****************************************************************************/
+static inline void
+chipcHw_vpmPhaseAlignInterruptMode(chipcHw_VPM_HW_PHASE_INTR_e mode)
+{
+ REG_LOCAL_IRQ_SAVE;
+ if (mode == chipcHw_VPM_HW_PHASE_INTR_DISABLE) {
+ pChipcHw->Spare1 &= ~chipcHw_REG_SPARE1_VPM_PHASE_INTR_ENABLE;
+ } else {
+ pChipcHw->Spare1 |= chipcHw_REG_SPARE1_VPM_PHASE_INTR_ENABLE;
+ }
+ pChipcHw->VPMPhaseCtrl2 =
+ (pChipcHw->
+ VPMPhaseCtrl2 & ~(chipcHw_REG_VPM_INTR_SELECT_MASK <<
+ chipcHw_REG_VPM_INTR_SELECT_SHIFT)) | mode;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Enable DDR phase alignment in software
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrSwPhaseAlignEnable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->DDRPhaseCtrl1 |= chipcHw_REG_DDR_SW_PHASE_CTRL_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Disable DDR phase alignment in software
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrSwPhaseAlignDisable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->DDRPhaseCtrl1 &= ~chipcHw_REG_DDR_SW_PHASE_CTRL_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Enable DDR phase alignment in hardware
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrHwPhaseAlignEnable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->DDRPhaseCtrl1 |= chipcHw_REG_DDR_HW_PHASE_CTRL_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Disable DDR phase alignment in hardware
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrHwPhaseAlignDisable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->DDRPhaseCtrl1 &= ~chipcHw_REG_DDR_HW_PHASE_CTRL_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Enable VPM phase alignment in software
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmSwPhaseAlignEnable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->VPMPhaseCtrl1 |= chipcHw_REG_VPM_SW_PHASE_CTRL_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Disable VPM phase alignment in software
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmSwPhaseAlignDisable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->VPMPhaseCtrl1 &= ~chipcHw_REG_VPM_SW_PHASE_CTRL_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Enable VPM phase alignment in hardware
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmHwPhaseAlignEnable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->VPMPhaseCtrl1 |= chipcHw_REG_VPM_HW_PHASE_CTRL_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Disable VPM phase alignment in hardware
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmHwPhaseAlignDisable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->VPMPhaseCtrl1 &= ~chipcHw_REG_VPM_HW_PHASE_CTRL_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Set DDR phase alignment margin in hardware
+*
+*/
+/****************************************************************************/
+static inline void
+chipcHw_setDdrHwPhaseAlignMargin(chipcHw_DDR_HW_PHASE_MARGIN_e margin)
+{
+ uint32_t ge = 0;
+ uint32_t le = 0;
+
+ switch (margin) {
+ case chipcHw_DDR_HW_PHASE_MARGIN_STRICT:
+ ge = 0x0F;
+ le = 0x0F;
+ break;
+ case chipcHw_DDR_HW_PHASE_MARGIN_MEDIUM:
+ ge = 0x03;
+ le = 0x3F;
+ break;
+ case chipcHw_DDR_HW_PHASE_MARGIN_WIDE:
+ ge = 0x01;
+ le = 0x7F;
+ break;
+ }
+
+ {
+ REG_LOCAL_IRQ_SAVE;
+
+ pChipcHw->DDRPhaseCtrl1 &=
+ ~((chipcHw_REG_DDR_PHASE_VALUE_GE_MASK <<
+ chipcHw_REG_DDR_PHASE_VALUE_GE_SHIFT)
+ || (chipcHw_REG_DDR_PHASE_VALUE_LE_MASK <<
+ chipcHw_REG_DDR_PHASE_VALUE_LE_SHIFT));
+
+ pChipcHw->DDRPhaseCtrl1 |=
+ ((ge << chipcHw_REG_DDR_PHASE_VALUE_GE_SHIFT)
+ || (le << chipcHw_REG_DDR_PHASE_VALUE_LE_SHIFT));
+
+ REG_LOCAL_IRQ_RESTORE;
+ }
+}
+
+/****************************************************************************/
+/**
+* @brief Set VPM phase alignment margin in hardware
+*
+*/
+/****************************************************************************/
+static inline void
+chipcHw_setVpmHwPhaseAlignMargin(chipcHw_VPM_HW_PHASE_MARGIN_e margin)
+{
+ uint32_t ge = 0;
+ uint32_t le = 0;
+
+ switch (margin) {
+ case chipcHw_VPM_HW_PHASE_MARGIN_STRICT:
+ ge = 0x0F;
+ le = 0x0F;
+ break;
+ case chipcHw_VPM_HW_PHASE_MARGIN_MEDIUM:
+ ge = 0x03;
+ le = 0x3F;
+ break;
+ case chipcHw_VPM_HW_PHASE_MARGIN_WIDE:
+ ge = 0x01;
+ le = 0x7F;
+ break;
+ }
+
+ {
+ REG_LOCAL_IRQ_SAVE;
+
+ pChipcHw->VPMPhaseCtrl1 &=
+ ~((chipcHw_REG_VPM_PHASE_VALUE_GE_MASK <<
+ chipcHw_REG_VPM_PHASE_VALUE_GE_SHIFT)
+ || (chipcHw_REG_VPM_PHASE_VALUE_LE_MASK <<
+ chipcHw_REG_VPM_PHASE_VALUE_LE_SHIFT));
+
+ pChipcHw->VPMPhaseCtrl1 |=
+ ((ge << chipcHw_REG_VPM_PHASE_VALUE_GE_SHIFT)
+ || (le << chipcHw_REG_VPM_PHASE_VALUE_LE_SHIFT));
+
+ REG_LOCAL_IRQ_RESTORE;
+ }
+}
+
+/****************************************************************************/
+/**
+* @brief Checks DDR phase aligned status done by HW
+*
+* @return 1: When aligned
+* 0: When not aligned
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_isDdrHwPhaseAligned(void)
+{
+ return (pChipcHw->
+ PhaseAlignStatus & chipcHw_REG_DDR_PHASE_ALIGNED) ? 1 : 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Checks VPM phase aligned status done by HW
+*
+* @return 1: When aligned
+* 0: When not aligned
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_isVpmHwPhaseAligned(void)
+{
+ return (pChipcHw->
+ PhaseAlignStatus & chipcHw_REG_VPM_PHASE_ALIGNED) ? 1 : 0;
+}
+
+/****************************************************************************/
+/**
+* @brief Get DDR phase aligned status done by HW
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getDdrHwPhaseAlignStatus(void)
+{
+ return (pChipcHw->
+ PhaseAlignStatus & chipcHw_REG_DDR_PHASE_STATUS_MASK) >>
+ chipcHw_REG_DDR_PHASE_STATUS_SHIFT;
+}
+
+/****************************************************************************/
+/**
+* @brief Get VPM phase aligned status done by HW
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getVpmHwPhaseAlignStatus(void)
+{
+ return (pChipcHw->
+ PhaseAlignStatus & chipcHw_REG_VPM_PHASE_STATUS_MASK) >>
+ chipcHw_REG_VPM_PHASE_STATUS_SHIFT;
+}
+
+/****************************************************************************/
+/**
+* @brief Get DDR phase control value
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getDdrPhaseControl(void)
+{
+ return (pChipcHw->
+ PhaseAlignStatus & chipcHw_REG_DDR_PHASE_CTRL_MASK) >>
+ chipcHw_REG_DDR_PHASE_CTRL_SHIFT;
+}
+
+/****************************************************************************/
+/**
+* @brief Get VPM phase control value
+*
+*/
+/****************************************************************************/
+static inline uint32_t chipcHw_getVpmPhaseControl(void)
+{
+ return (pChipcHw->
+ PhaseAlignStatus & chipcHw_REG_VPM_PHASE_CTRL_MASK) >>
+ chipcHw_REG_VPM_PHASE_CTRL_SHIFT;
+}
+
+/****************************************************************************/
+/**
+* @brief DDR phase alignment timeout count
+*
+* @note If HW fails to perform the phase alignment, it will trigger
+* a DDR phase alignment timeout interrupt.
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrHwPhaseAlignTimeout(uint32_t busCycle)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->DDRPhaseCtrl2 &=
+ ~(chipcHw_REG_DDR_PHASE_TIMEOUT_COUNT_MASK <<
+ chipcHw_REG_DDR_PHASE_TIMEOUT_COUNT_SHIFT);
+ pChipcHw->DDRPhaseCtrl2 |=
+ (busCycle & chipcHw_REG_DDR_PHASE_TIMEOUT_COUNT_MASK) <<
+ chipcHw_REG_DDR_PHASE_TIMEOUT_COUNT_SHIFT;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief VPM phase alignment timeout count
+*
+* @note If HW fails to perform the phase alignment, it will trigger
+* a VPM phase alignment timeout interrupt.
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmHwPhaseAlignTimeout(uint32_t busCycle)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->VPMPhaseCtrl2 &=
+ ~(chipcHw_REG_VPM_PHASE_TIMEOUT_COUNT_MASK <<
+ chipcHw_REG_VPM_PHASE_TIMEOUT_COUNT_SHIFT);
+ pChipcHw->VPMPhaseCtrl2 |=
+ (busCycle & chipcHw_REG_VPM_PHASE_TIMEOUT_COUNT_MASK) <<
+ chipcHw_REG_VPM_PHASE_TIMEOUT_COUNT_SHIFT;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Clear DDR phase alignment timeout interrupt
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrHwPhaseAlignTimeoutInterruptClear(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ /* Clear timeout interrupt service bit */
+ pChipcHw->DDRPhaseCtrl2 |= chipcHw_REG_DDR_INTR_SERVICED;
+ pChipcHw->DDRPhaseCtrl2 &= ~chipcHw_REG_DDR_INTR_SERVICED;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief Clear VPM phase alignment timeout interrupt
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmHwPhaseAlignTimeoutInterruptClear(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ /* Clear timeout interrupt service bit */
+ pChipcHw->VPMPhaseCtrl2 |= chipcHw_REG_VPM_INTR_SERVICED;
+ pChipcHw->VPMPhaseCtrl2 &= ~chipcHw_REG_VPM_INTR_SERVICED;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief DDR phase alignment timeout interrupt enable
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrHwPhaseAlignTimeoutInterruptEnable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ chipcHw_ddrHwPhaseAlignTimeoutInterruptClear(); /* Recommended */
+ /* Enable timeout interrupt */
+ pChipcHw->DDRPhaseCtrl2 |= chipcHw_REG_DDR_TIMEOUT_INTR_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief VPM phase alignment timeout interrupt enable
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmHwPhaseAlignTimeoutInterruptEnable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ chipcHw_vpmHwPhaseAlignTimeoutInterruptClear(); /* Recommended */
+ /* Enable timeout interrupt */
+ pChipcHw->VPMPhaseCtrl2 |= chipcHw_REG_VPM_TIMEOUT_INTR_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief DDR phase alignment timeout interrupt disable
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_ddrHwPhaseAlignTimeoutInterruptDisable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->DDRPhaseCtrl2 &= ~chipcHw_REG_DDR_TIMEOUT_INTR_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+/****************************************************************************/
+/**
+* @brief VPM phase alignment timeout interrupt disable
+*
+*/
+/****************************************************************************/
+static inline void chipcHw_vpmHwPhaseAlignTimeoutInterruptDisable(void)
+{
+ REG_LOCAL_IRQ_SAVE;
+ pChipcHw->VPMPhaseCtrl2 &= ~chipcHw_REG_VPM_TIMEOUT_INTR_ENABLE;
+ REG_LOCAL_IRQ_RESTORE;
+}
+
+#endif /* CHIPC_INLINE_H */
diff --git a/arch/arm/mach-bcmring/include/mach/csp/chipcHw_reg.h b/arch/arm/mach-bcmring/include/mach/csp/chipcHw_reg.h
new file mode 100644
index 00000000000..b162448f613
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/chipcHw_reg.h
@@ -0,0 +1,530 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file chipcHw_reg.h
+*
+* @brief Definitions for low level chip control registers
+*
+*/
+/****************************************************************************/
+#ifndef CHIPCHW_REG_H
+#define CHIPCHW_REG_H
+
+#include <mach/csp/mm_io.h>
+#include <csp/reg.h>
+#include <mach/csp/ddrcReg.h>
+
+#define chipcHw_BASE_ADDRESS MM_IO_BASE_CHIPC
+
+typedef struct {
+ uint32_t ChipId; /* Chip ID */
+ uint32_t DDRClock; /* PLL1 Channel 1 for DDR clock */
+ uint32_t ARMClock; /* PLL1 Channel 2 for ARM clock */
+ uint32_t ESWClock; /* PLL1 Channel 3 for ESW system clock */
+ uint32_t VPMClock; /* PLL1 Channel 4 for VPM clock */
+ uint32_t ESW125Clock; /* PLL1 Channel 5 for ESW 125MHz clock */
+ uint32_t UARTClock; /* PLL1 Channel 6 for UART clock */
+ uint32_t SDIO0Clock; /* PLL1 Channel 7 for SDIO 0 clock */
+ uint32_t SDIO1Clock; /* PLL1 Channel 8 for SDIO 1 clock */
+ uint32_t SPIClock; /* PLL1 Channel 9 for SPI master Clock */
+ uint32_t ETMClock; /* PLL1 Channel 10 for ARM ETM Clock */
+
+ uint32_t ACLKClock; /* ACLK Clock (Divider) */
+ uint32_t OTPClock; /* OTP Clock (Divider) */
+ uint32_t I2CClock; /* I2C Clock (CK_13m) (Divider) */
+ uint32_t I2S0Clock; /* I2S0 Clock (Divider) */
+ uint32_t RTBUSClock; /* RTBUS (DDR PHY Config.) Clock (Divider) */
+ uint32_t pad1;
+ uint32_t APM100Clock; /* APM 100MHz CLK Clock (Divider) */
+ uint32_t TSCClock; /* TSC Clock (Divider) */
+ uint32_t LEDClock; /* LED Clock (Divider) */
+
+ uint32_t USBClock; /* PLL2 Channel 1 for USB clock */
+ uint32_t LCDClock; /* PLL2 Channel 2 for LCD clock */
+ uint32_t APMClock; /* PLL2 Channel 3 for APM 200 MHz clock */
+
+ uint32_t BusIntfClock; /* Bus interface clock */
+
+ uint32_t PLLStatus; /* PLL status register (PLL1) */
+ uint32_t PLLConfig; /* PLL configuration register (PLL1) */
+ uint32_t PLLPreDivider; /* PLL pre-divider control register (PLL1) */
+ uint32_t PLLDivider; /* PLL divider control register (PLL1) */
+ uint32_t PLLControl1; /* PLL analog control register #1 (PLL1) */
+ uint32_t PLLControl2; /* PLL analog control register #2 (PLL1) */
+
+ uint32_t I2S1Clock; /* I2S1 Clock */
+ uint32_t AudioEnable; /* Enable/ disable audio channel */
+ uint32_t SoftReset1; /* Reset blocks */
+ uint32_t SoftReset2; /* Reset blocks */
+ uint32_t Spare1; /* Phase align interrupts */
+ uint32_t Sticky; /* Sticky bits */
+ uint32_t MiscCtrl; /* Misc. control */
+ uint32_t pad3[3];
+
+ uint32_t PLLStatus2; /* PLL status register (PLL2) */
+ uint32_t PLLConfig2; /* PLL configuration register (PLL2) */
+ uint32_t PLLPreDivider2; /* PLL pre-divider control register (PLL2) */
+ uint32_t PLLDivider2; /* PLL divider control register (PLL2) */
+ uint32_t PLLControl12; /* PLL analog control register #1 (PLL2) */
+ uint32_t PLLControl22; /* PLL analog control register #2 (PLL2) */
+
+ uint32_t DDRPhaseCtrl1; /* DDR Clock Phase Alignment control1 */
+ uint32_t VPMPhaseCtrl1; /* VPM Clock Phase Alignment control1 */
+ uint32_t PhaseAlignStatus; /* DDR/VPM Clock Phase Alignment Status */
+ uint32_t PhaseCtrlStatus; /* DDR/VPM Clock HW DDR/VPM ph_ctrl and load_ch Status */
+ uint32_t DDRPhaseCtrl2; /* DDR Clock Phase Alignment control2 */
+ uint32_t VPMPhaseCtrl2; /* VPM Clock Phase Alignment control2 */
+ uint32_t pad4[9];
+
+ uint32_t SoftOTP1; /* Software OTP control */
+ uint32_t SoftOTP2; /* Software OTP control */
+ uint32_t SoftStraps; /* Software strap */
+ uint32_t PinStraps; /* Pin Straps */
+ uint32_t DiffOscCtrl; /* Diff oscillator control */
+ uint32_t DiagsCtrl; /* Diagnostic control */
+ uint32_t DiagsOutputCtrl; /* Diagnostic output enable */
+ uint32_t DiagsReadBackCtrl; /* Diagnostic read back control */
+
+ uint32_t LcdPifMode; /* LCD/PIF Pin Sharing MUX Mode */
+
+ uint32_t GpioMux_0_7; /* Pin Sharing MUX0 Control */
+ uint32_t GpioMux_8_15; /* Pin Sharing MUX1 Control */
+ uint32_t GpioMux_16_23; /* Pin Sharing MUX2 Control */
+ uint32_t GpioMux_24_31; /* Pin Sharing MUX3 Control */
+ uint32_t GpioMux_32_39; /* Pin Sharing MUX4 Control */
+ uint32_t GpioMux_40_47; /* Pin Sharing MUX5 Control */
+ uint32_t GpioMux_48_55; /* Pin Sharing MUX6 Control */
+ uint32_t GpioMux_56_63; /* Pin Sharing MUX7 Control */
+
+ uint32_t GpioSR_0_7; /* Slew rate for GPIO 0 - 7 */
+ uint32_t GpioSR_8_15; /* Slew rate for GPIO 8 - 15 */
+ uint32_t GpioSR_16_23; /* Slew rate for GPIO 16 - 23 */
+ uint32_t GpioSR_24_31; /* Slew rate for GPIO 24 - 31 */
+ uint32_t GpioSR_32_39; /* Slew rate for GPIO 32 - 39 */
+ uint32_t GpioSR_40_47; /* Slew rate for GPIO 40 - 47 */
+ uint32_t GpioSR_48_55; /* Slew rate for GPIO 48 - 55 */
+ uint32_t GpioSR_56_63; /* Slew rate for GPIO 56 - 63 */
+ uint32_t MiscSR_0_7; /* Slew rate for MISC 0 - 7 */
+ uint32_t MiscSR_8_15; /* Slew rate for MISC 8 - 15 */
+
+ uint32_t GpioPull_0_15; /* Pull up registers for GPIO 0 - 15 */
+ uint32_t GpioPull_16_31; /* Pull up registers for GPIO 16 - 31 */
+ uint32_t GpioPull_32_47; /* Pull up registers for GPIO 32 - 47 */
+ uint32_t GpioPull_48_63; /* Pull up registers for GPIO 48 - 63 */
+ uint32_t MiscPull_0_15; /* Pull up registers for MISC 0 - 15 */
+
+ uint32_t GpioInput_0_31; /* Input type for GPIO 0 - 31 */
+ uint32_t GpioInput_32_63; /* Input type for GPIO 32 - 63 */
+ uint32_t MiscInput_0_15; /* Input type for MISC 0 - 16 */
+} chipcHw_REG_t;
+
+#define pChipcHw ((volatile chipcHw_REG_t *) chipcHw_BASE_ADDRESS)
+#define pChipcPhysical ((volatile chipcHw_REG_t *) MM_ADDR_IO_CHIPC)
+
+#define chipcHw_REG_CHIPID_BASE_MASK 0xFFFFF000
+#define chipcHw_REG_CHIPID_BASE_SHIFT 12
+#define chipcHw_REG_CHIPID_REV_MASK 0x00000FFF
+#define chipcHw_REG_REV_A0 0xA00
+#define chipcHw_REG_REV_B0 0x0B0
+
+#define chipcHw_REG_PLL_STATUS_CONTROL_ENABLE 0x80000000 /* Allow controlling PLL registers */
+#define chipcHw_REG_PLL_STATUS_LOCKED 0x00000001 /* PLL is settled */
+#define chipcHw_REG_PLL_CONFIG_D_RESET 0x00000008 /* Digital reset */
+#define chipcHw_REG_PLL_CONFIG_A_RESET 0x00000004 /* Analog reset */
+#define chipcHw_REG_PLL_CONFIG_BYPASS_ENABLE 0x00000020 /* Bypass enable */
+#define chipcHw_REG_PLL_CONFIG_OUTPUT_ENABLE 0x00000010 /* Output enable */
+#define chipcHw_REG_PLL_CONFIG_POWER_DOWN 0x00000001 /* Power down */
+#define chipcHw_REG_PLL_CONFIG_VCO_SPLIT_FREQ 1600000000 /* 1.6GHz VCO split frequency */
+#define chipcHw_REG_PLL_CONFIG_VCO_800_1600 0x00000000 /* VCO range 800-1600 MHz */
+#define chipcHw_REG_PLL_CONFIG_VCO_1601_3200 0x00000080 /* VCO range 1601-3200 MHz */
+#define chipcHw_REG_PLL_CONFIG_TEST_ENABLE 0x00010000 /* PLL test output enable */
+#define chipcHw_REG_PLL_CONFIG_TEST_SELECT_MASK 0x003E0000 /* Mask to set test values */
+#define chipcHw_REG_PLL_CONFIG_TEST_SELECT_SHIFT 17
+
+#define chipcHw_REG_PLL_CLOCK_PHASE_COMP 0x00800000 /* Phase comparator output */
+#define chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_MASK 0x00300000 /* Clock to bus ratio mask */
+#define chipcHw_REG_PLL_CLOCK_TO_BUS_RATIO_SHIFT 20 /* Number of bits to be shifted */
+#define chipcHw_REG_PLL_CLOCK_POWER_DOWN 0x00080000 /* PLL channel power down */
+#define chipcHw_REG_PLL_CLOCK_SOURCE_GPIO 0x00040000 /* Use GPIO as source */
+#define chipcHw_REG_PLL_CLOCK_BYPASS_SELECT 0x00020000 /* Select bypass clock */
+#define chipcHw_REG_PLL_CLOCK_OUTPUT_ENABLE 0x00010000 /* Clock gated ON */
+#define chipcHw_REG_PLL_CLOCK_PHASE_UPDATE_ENABLE 0x00008000 /* Clock phase update enable */
+#define chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_SHIFT 8 /* Number of bits to be shifted */
+#define chipcHw_REG_PLL_CLOCK_PHASE_CONTROL_MASK 0x00003F00 /* Phase control mask */
+#define chipcHw_REG_PLL_CLOCK_MDIV_MASK 0x000000FF /* Clock post divider mask
+
+ 00000000 = divide-by-256
+ 00000001 = divide-by-1
+ 00000010 = divide-by-2
+ 00000011 = divide-by-3
+ 00000100 = divide-by-4
+ 00000101 = divide-by-5
+ 00000110 = divide-by-6
+ .
+ .
+ 11111011 = divide-by-251
+ 11111100 = divide-by-252
+ 11111101 = divide-by-253
+ 11111110 = divide-by-254
+ */
+
+#define chipcHw_REG_DIV_CLOCK_SOURCE_OTHER 0x00040000 /* NON-PLL clock source select */
+#define chipcHw_REG_DIV_CLOCK_BYPASS_SELECT 0x00020000 /* NON-PLL clock bypass enable */
+#define chipcHw_REG_DIV_CLOCK_OUTPUT_ENABLE 0x00010000 /* NON-PLL clock output enable */
+#define chipcHw_REG_DIV_CLOCK_DIV_MASK 0x000000FF /* NON-PLL clock post-divide mask */
+#define chipcHw_REG_DIV_CLOCK_DIV_256 0x00000000 /* NON-PLL clock post-divide by 256 */
+
+#define chipcHw_REG_PLL_PREDIVIDER_P1_SHIFT 0
+#define chipcHw_REG_PLL_PREDIVIDER_P2_SHIFT 4
+#define chipcHw_REG_PLL_PREDIVIDER_NDIV_SHIFT 8
+#define chipcHw_REG_PLL_PREDIVIDER_NDIV_MASK 0x0001FF00
+#define chipcHw_REG_PLL_PREDIVIDER_POWER_DOWN 0x02000000
+#define chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MASK 0x00700000 /* Divider mask */
+#define chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_INTEGER 0x00000000 /* Integer-N Mode */
+#define chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MASH_UNIT 0x00100000 /* MASH Sigma-Delta Modulator Unit Mode */
+#define chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MFB_UNIT 0x00200000 /* MFB Sigma-Delta Modulator Unit Mode */
+#define chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MASH_1_8 0x00300000 /* MASH Sigma-Delta Modulator 1/8 Mode */
+#define chipcHw_REG_PLL_PREDIVIDER_NDIV_MODE_MFB_1_8 0x00400000 /* MFB Sigma-Delta Modulator 1/8 Mode */
+
+#define chipcHw_REG_PLL_PREDIVIDER_NDIV_i(vco) ((vco) / chipcHw_XTAL_FREQ_Hz)
+#define chipcHw_REG_PLL_PREDIVIDER_P1 1
+#define chipcHw_REG_PLL_PREDIVIDER_P2 1
+
+#define chipcHw_REG_PLL_DIVIDER_M1DIV 0x03000000
+#define chipcHw_REG_PLL_DIVIDER_FRAC 0x00FFFFFF /* Fractional divider */
+
+#define chipcHw_REG_PLL_DIVIDER_NDIV_f_SS (0x00FFFFFF) /* To attain spread with max frequency */
+
+#define chipcHw_REG_PLL_DIVIDER_NDIV_f 0 /* ndiv_frac = chipcHw_REG_PLL_DIVIDER_NDIV_f /
+ chipcHw_REG_PLL_DIVIDER_FRAC
+ = 0, when SS is disable
+ */
+
+#define chipcHw_REG_PLL_DIVIDER_MDIV(vco, Hz) ((chipcHw_divide((vco), (Hz)) > 255) ? 0 : chipcHw_divide((vco), (Hz)))
+
+#define chipcHw_REG_ACLKClock_CLK_DIV_MASK 0x3
+
+/* System booting strap options */
+#define chipcHw_STRAPS_SOFT_OVERRIDE 0x00000001 /* Software Strap Override */
+
+#define chipcHw_STRAPS_BOOT_DEVICE_NAND_FLASH_8 0x00000000 /* 8 bit NAND FLASH Boot */
+#define chipcHw_STRAPS_BOOT_DEVICE_NOR_FLASH_16 0x00000002 /* 16 bit NOR FLASH Boot */
+#define chipcHw_STRAPS_BOOT_DEVICE_SERIAL_FLASH 0x00000004 /* Serial FLASH Boot */
+#define chipcHw_STRAPS_BOOT_DEVICE_NAND_FLASH_16 0x00000006 /* 16 bit NAND FLASH Boot */
+#define chipcHw_STRAPS_BOOT_DEVICE_UART 0x00000008 /* UART Boot */
+#define chipcHw_STRAPS_BOOT_DEVICE_MASK 0x0000000E /* Mask */
+
+/* System boot option */
+#define chipcHw_STRAPS_BOOT_OPTION_BROM 0x00000000 /* Boot from Boot ROM */
+#define chipcHw_STRAPS_BOOT_OPTION_ARAM 0x00000020 /* Boot from ARAM */
+#define chipcHw_STRAPS_BOOT_OPTION_NOR 0x00000030 /* Boot from NOR flash */
+
+/* NAND Flash page size strap options */
+#define chipcHw_STRAPS_NAND_PAGESIZE_512 0x00000000 /* NAND FLASH page size of 512 bytes */
+#define chipcHw_STRAPS_NAND_PAGESIZE_2048 0x00000040 /* NAND FLASH page size of 2048 bytes */
+#define chipcHw_STRAPS_NAND_PAGESIZE_4096 0x00000080 /* NAND FLASH page size of 4096 bytes */
+#define chipcHw_STRAPS_NAND_PAGESIZE_EXT 0x000000C0 /* NAND FLASH page of extened size */
+#define chipcHw_STRAPS_NAND_PAGESIZE_MASK 0x000000C0 /* Mask */
+
+#define chipcHw_STRAPS_NAND_EXTRA_CYCLE 0x00000400 /* NAND FLASH address cycle configuration */
+#define chipcHw_STRAPS_REBOOT_TO_UART 0x00000800 /* Reboot to UART on error */
+
+/* Secure boot mode strap options */
+#define chipcHw_STRAPS_BOOT_MODE_NORMAL 0x00000000 /* Normal Boot */
+#define chipcHw_STRAPS_BOOT_MODE_DBG_SW 0x00000100 /* Software debugging Boot */
+#define chipcHw_STRAPS_BOOT_MODE_DBG_BOOT 0x00000200 /* Boot rom debugging Boot */
+#define chipcHw_STRAPS_BOOT_MODE_NORMAL_QUIET 0x00000300 /* Normal Boot (Quiet BootRom) */
+#define chipcHw_STRAPS_BOOT_MODE_MASK 0x00000300 /* Mask */
+
+/* Slave Mode straps */
+#define chipcHw_STRAPS_I2CS 0x02000000 /* I2C Slave */
+#define chipcHw_STRAPS_SPIS 0x01000000 /* SPI Slave */
+
+/* Strap pin options */
+#define chipcHw_REG_SW_STRAPS ((pChipcHw->PinStraps & 0x0000FC00) >> 10)
+
+/* PIF/LCD pin sharing defines */
+#define chipcHw_REG_LCD_PIN_ENABLE 0x00000001 /* LCD Controller is used and the pins have LCD functions */
+#define chipcHw_REG_PIF_PIN_ENABLE 0x00000002 /* LCD pins are used to perform PIF functions */
+
+#define chipcHw_GPIO_COUNT 61 /* Number of GPIO pin accessible thorugh CHIPC */
+
+/* NOTE: Any changes to these constants will require a corresponding change to chipcHw_str.c */
+#define chipcHw_REG_GPIO_MUX_KEYPAD 0x00000001 /* GPIO mux for Keypad */
+#define chipcHw_REG_GPIO_MUX_I2CH 0x00000002 /* GPIO mux for I2CH */
+#define chipcHw_REG_GPIO_MUX_SPI 0x00000003 /* GPIO mux for SPI */
+#define chipcHw_REG_GPIO_MUX_UART 0x00000004 /* GPIO mux for UART */
+#define chipcHw_REG_GPIO_MUX_LEDMTXP 0x00000005 /* GPIO mux for LEDMTXP */
+#define chipcHw_REG_GPIO_MUX_LEDMTXS 0x00000006 /* GPIO mux for LEDMTXS */
+#define chipcHw_REG_GPIO_MUX_SDIO0 0x00000007 /* GPIO mux for SDIO0 */
+#define chipcHw_REG_GPIO_MUX_SDIO1 0x00000008 /* GPIO mux for SDIO1 */
+#define chipcHw_REG_GPIO_MUX_PCM 0x00000009 /* GPIO mux for PCM */
+#define chipcHw_REG_GPIO_MUX_I2S 0x0000000A /* GPIO mux for I2S */
+#define chipcHw_REG_GPIO_MUX_ETM 0x0000000B /* GPIO mux for ETM */
+#define chipcHw_REG_GPIO_MUX_DEBUG 0x0000000C /* GPIO mux for DEBUG */
+#define chipcHw_REG_GPIO_MUX_MISC 0x0000000D /* GPIO mux for MISC */
+#define chipcHw_REG_GPIO_MUX_GPIO 0x00000000 /* GPIO mux for GPIO */
+#define chipcHw_REG_GPIO_MUX(pin) (&pChipcHw->GpioMux_0_7 + ((pin) >> 3))
+#define chipcHw_REG_GPIO_MUX_POSITION(pin) (((pin) & 0x00000007) << 2)
+#define chipcHw_REG_GPIO_MUX_MASK 0x0000000F /* Mask */
+
+#define chipcHw_REG_SLEW_RATE_HIGH 0x00000000 /* High speed slew rate */
+#define chipcHw_REG_SLEW_RATE_NORMAL 0x00000008 /* Normal slew rate */
+ /* Pins beyond 42 are defined by skipping 8 bits within the register */
+#define chipcHw_REG_SLEW_RATE(pin) (((pin) > 42) ? (&pChipcHw->GpioSR_0_7 + (((pin) + 2) >> 3)) : (&pChipcHw->GpioSR_0_7 + ((pin) >> 3)))
+#define chipcHw_REG_SLEW_RATE_POSITION(pin) (((pin) > 42) ? ((((pin) + 2) & 0x00000007) << 2) : (((pin) & 0x00000007) << 2))
+#define chipcHw_REG_SLEW_RATE_MASK 0x00000008 /* Mask */
+
+#define chipcHw_REG_CURRENT_STRENGTH_2mA 0x00000001 /* Current driving strength 2 milli ampere */
+#define chipcHw_REG_CURRENT_STRENGTH_4mA 0x00000002 /* Current driving strength 4 milli ampere */
+#define chipcHw_REG_CURRENT_STRENGTH_6mA 0x00000004 /* Current driving strength 6 milli ampere */
+#define chipcHw_REG_CURRENT_STRENGTH_8mA 0x00000005 /* Current driving strength 8 milli ampere */
+#define chipcHw_REG_CURRENT_STRENGTH_10mA 0x00000006 /* Current driving strength 10 milli ampere */
+#define chipcHw_REG_CURRENT_STRENGTH_12mA 0x00000007 /* Current driving strength 12 milli ampere */
+#define chipcHw_REG_CURRENT_MASK 0x00000007 /* Mask */
+ /* Pins beyond 42 are defined by skipping 8 bits */
+#define chipcHw_REG_CURRENT(pin) (((pin) > 42) ? (&pChipcHw->GpioSR_0_7 + (((pin) + 2) >> 3)) : (&pChipcHw->GpioSR_0_7 + ((pin) >> 3)))
+#define chipcHw_REG_CURRENT_POSITION(pin) (((pin) > 42) ? ((((pin) + 2) & 0x00000007) << 2) : (((pin) & 0x00000007) << 2))
+
+#define chipcHw_REG_PULL_NONE 0x00000000 /* No pull up register */
+#define chipcHw_REG_PULL_UP 0x00000001 /* Pull up register enable */
+#define chipcHw_REG_PULL_DOWN 0x00000002 /* Pull down register enable */
+#define chipcHw_REG_PULLUP_MASK 0x00000003 /* Mask */
+ /* Pins beyond 42 are defined by skipping 4 bits */
+#define chipcHw_REG_PULLUP(pin) (((pin) > 42) ? (&pChipcHw->GpioPull_0_15 + (((pin) + 2) >> 4)) : (&pChipcHw->GpioPull_0_15 + ((pin) >> 4)))
+#define chipcHw_REG_PULLUP_POSITION(pin) (((pin) > 42) ? ((((pin) + 2) & 0x0000000F) << 1) : (((pin) & 0x0000000F) << 1))
+
+#define chipcHw_REG_INPUTTYPE_CMOS 0x00000000 /* Normal CMOS logic */
+#define chipcHw_REG_INPUTTYPE_ST 0x00000001 /* High speed Schmitt Trigger */
+#define chipcHw_REG_INPUTTYPE_MASK 0x00000001 /* Mask */
+ /* Pins beyond 42 are defined by skipping 2 bits */
+#define chipcHw_REG_INPUTTYPE(pin) (((pin) > 42) ? (&pChipcHw->GpioInput_0_31 + (((pin) + 2) >> 5)) : (&pChipcHw->GpioInput_0_31 + ((pin) >> 5)))
+#define chipcHw_REG_INPUTTYPE_POSITION(pin) (((pin) > 42) ? ((((pin) + 2) & 0x0000001F)) : (((pin) & 0x0000001F)))
+
+/* Device connected to the bus clock */
+#define chipcHw_REG_BUS_CLOCK_ARM 0x00000001 /* Bus interface clock for ARM */
+#define chipcHw_REG_BUS_CLOCK_VDEC 0x00000002 /* Bus interface clock for VDEC */
+#define chipcHw_REG_BUS_CLOCK_ARAM 0x00000004 /* Bus interface clock for ARAM */
+#define chipcHw_REG_BUS_CLOCK_HPM 0x00000008 /* Bus interface clock for HPM */
+#define chipcHw_REG_BUS_CLOCK_DDRC 0x00000010 /* Bus interface clock for DDRC */
+#define chipcHw_REG_BUS_CLOCK_DMAC0 0x00000020 /* Bus interface clock for DMAC0 */
+#define chipcHw_REG_BUS_CLOCK_DMAC1 0x00000040 /* Bus interface clock for DMAC1 */
+#define chipcHw_REG_BUS_CLOCK_NVI 0x00000080 /* Bus interface clock for NVI */
+#define chipcHw_REG_BUS_CLOCK_ESW 0x00000100 /* Bus interface clock for ESW */
+#define chipcHw_REG_BUS_CLOCK_GE 0x00000200 /* Bus interface clock for GE */
+#define chipcHw_REG_BUS_CLOCK_I2CH 0x00000400 /* Bus interface clock for I2CH */
+#define chipcHw_REG_BUS_CLOCK_I2S0 0x00000800 /* Bus interface clock for I2S0 */
+#define chipcHw_REG_BUS_CLOCK_I2S1 0x00001000 /* Bus interface clock for I2S1 */
+#define chipcHw_REG_BUS_CLOCK_VRAM 0x00002000 /* Bus interface clock for VRAM */
+#define chipcHw_REG_BUS_CLOCK_CLCD 0x00004000 /* Bus interface clock for CLCD */
+#define chipcHw_REG_BUS_CLOCK_LDK 0x00008000 /* Bus interface clock for LDK */
+#define chipcHw_REG_BUS_CLOCK_LED 0x00010000 /* Bus interface clock for LED */
+#define chipcHw_REG_BUS_CLOCK_OTP 0x00020000 /* Bus interface clock for OTP */
+#define chipcHw_REG_BUS_CLOCK_PIF 0x00040000 /* Bus interface clock for PIF */
+#define chipcHw_REG_BUS_CLOCK_SPU 0x00080000 /* Bus interface clock for SPU */
+#define chipcHw_REG_BUS_CLOCK_SDIO0 0x00100000 /* Bus interface clock for SDIO0 */
+#define chipcHw_REG_BUS_CLOCK_SDIO1 0x00200000 /* Bus interface clock for SDIO1 */
+#define chipcHw_REG_BUS_CLOCK_SPIH 0x00400000 /* Bus interface clock for SPIH */
+#define chipcHw_REG_BUS_CLOCK_SPIS 0x00800000 /* Bus interface clock for SPIS */
+#define chipcHw_REG_BUS_CLOCK_UART0 0x01000000 /* Bus interface clock for UART0 */
+#define chipcHw_REG_BUS_CLOCK_UART1 0x02000000 /* Bus interface clock for UART1 */
+#define chipcHw_REG_BUS_CLOCK_BBL 0x04000000 /* Bus interface clock for BBL */
+#define chipcHw_REG_BUS_CLOCK_I2CS 0x08000000 /* Bus interface clock for I2CS */
+#define chipcHw_REG_BUS_CLOCK_USBH 0x10000000 /* Bus interface clock for USB Host */
+#define chipcHw_REG_BUS_CLOCK_USBD 0x20000000 /* Bus interface clock for USB Device */
+#define chipcHw_REG_BUS_CLOCK_BROM 0x40000000 /* Bus interface clock for Boot ROM */
+#define chipcHw_REG_BUS_CLOCK_TSC 0x80000000 /* Bus interface clock for Touch screen */
+
+/* Software resets defines */
+#define chipcHw_REG_SOFT_RESET_VPM_GLOBAL_HOLD 0x0000000080000000ULL /* Reset Global VPM and hold */
+#define chipcHw_REG_SOFT_RESET_VPM_HOLD 0x0000000040000000ULL /* Reset VPM and hold */
+#define chipcHw_REG_SOFT_RESET_VPM_GLOBAL 0x0000000020000000ULL /* Reset Global VPM */
+#define chipcHw_REG_SOFT_RESET_VPM 0x0000000010000000ULL /* Reset VPM */
+#define chipcHw_REG_SOFT_RESET_KEYPAD 0x0000000008000000ULL /* Reset Key pad */
+#define chipcHw_REG_SOFT_RESET_LED 0x0000000004000000ULL /* Reset LED */
+#define chipcHw_REG_SOFT_RESET_SPU 0x0000000002000000ULL /* Reset SPU */
+#define chipcHw_REG_SOFT_RESET_RNG 0x0000000001000000ULL /* Reset RNG */
+#define chipcHw_REG_SOFT_RESET_PKA 0x0000000000800000ULL /* Reset PKA */
+#define chipcHw_REG_SOFT_RESET_LCD 0x0000000000400000ULL /* Reset LCD */
+#define chipcHw_REG_SOFT_RESET_PIF 0x0000000000200000ULL /* Reset PIF */
+#define chipcHw_REG_SOFT_RESET_I2CS 0x0000000000100000ULL /* Reset I2C Slave */
+#define chipcHw_REG_SOFT_RESET_I2CH 0x0000000000080000ULL /* Reset I2C Host */
+#define chipcHw_REG_SOFT_RESET_SDIO1 0x0000000000040000ULL /* Reset SDIO 1 */
+#define chipcHw_REG_SOFT_RESET_SDIO0 0x0000000000020000ULL /* Reset SDIO 0 */
+#define chipcHw_REG_SOFT_RESET_BBL 0x0000000000010000ULL /* Reset BBL */
+#define chipcHw_REG_SOFT_RESET_I2S1 0x0000000000008000ULL /* Reset I2S1 */
+#define chipcHw_REG_SOFT_RESET_I2S0 0x0000000000004000ULL /* Reset I2S0 */
+#define chipcHw_REG_SOFT_RESET_SPIS 0x0000000000002000ULL /* Reset SPI Slave */
+#define chipcHw_REG_SOFT_RESET_SPIH 0x0000000000001000ULL /* Reset SPI Host */
+#define chipcHw_REG_SOFT_RESET_GPIO1 0x0000000000000800ULL /* Reset GPIO block 1 */
+#define chipcHw_REG_SOFT_RESET_GPIO0 0x0000000000000400ULL /* Reset GPIO block 0 */
+#define chipcHw_REG_SOFT_RESET_UART1 0x0000000000000200ULL /* Reset UART 1 */
+#define chipcHw_REG_SOFT_RESET_UART0 0x0000000000000100ULL /* Reset UART 0 */
+#define chipcHw_REG_SOFT_RESET_NVI 0x0000000000000080ULL /* Reset NVI */
+#define chipcHw_REG_SOFT_RESET_WDOG 0x0000000000000040ULL /* Reset Watch dog */
+#define chipcHw_REG_SOFT_RESET_TMR 0x0000000000000020ULL /* Reset Timer */
+#define chipcHw_REG_SOFT_RESET_ETM 0x0000000000000010ULL /* Reset ETM */
+#define chipcHw_REG_SOFT_RESET_ARM_HOLD 0x0000000000000008ULL /* Reset ARM and HOLD */
+#define chipcHw_REG_SOFT_RESET_ARM 0x0000000000000004ULL /* Reset ARM */
+#define chipcHw_REG_SOFT_RESET_CHIP_WARM 0x0000000000000002ULL /* Chip warm reset */
+#define chipcHw_REG_SOFT_RESET_CHIP_SOFT 0x0000000000000001ULL /* Chip soft reset */
+#define chipcHw_REG_SOFT_RESET_VDEC 0x0000100000000000ULL /* Video decoder */
+#define chipcHw_REG_SOFT_RESET_GE 0x0000080000000000ULL /* Graphics engine */
+#define chipcHw_REG_SOFT_RESET_OTP 0x0000040000000000ULL /* Reset OTP */
+#define chipcHw_REG_SOFT_RESET_USB2 0x0000020000000000ULL /* Reset USB2 */
+#define chipcHw_REG_SOFT_RESET_USB1 0x0000010000000000ULL /* Reset USB 1 */
+#define chipcHw_REG_SOFT_RESET_USB 0x0000008000000000ULL /* Reset USB 1 and USB2 soft reset */
+#define chipcHw_REG_SOFT_RESET_ESW 0x0000004000000000ULL /* Reset Ethernet switch */
+#define chipcHw_REG_SOFT_RESET_ESWCLK 0x0000002000000000ULL /* Reset Ethernet switch clock */
+#define chipcHw_REG_SOFT_RESET_DDRPHY 0x0000001000000000ULL /* Reset DDR Physical */
+#define chipcHw_REG_SOFT_RESET_DDR 0x0000000800000000ULL /* Reset DDR Controller */
+#define chipcHw_REG_SOFT_RESET_TSC 0x0000000400000000ULL /* Reset Touch screen */
+#define chipcHw_REG_SOFT_RESET_PCM 0x0000000200000000ULL /* Reset PCM device */
+#define chipcHw_REG_SOFT_RESET_APM 0x0000200100000000ULL /* Reset APM device */
+
+#define chipcHw_REG_SOFT_RESET_VPM_GLOBAL_UNHOLD 0x8000000000000000ULL /* Unhold Global VPM */
+#define chipcHw_REG_SOFT_RESET_VPM_UNHOLD 0x4000000000000000ULL /* Unhold VPM */
+#define chipcHw_REG_SOFT_RESET_ARM_UNHOLD 0x2000000000000000ULL /* Unhold ARM reset */
+#define chipcHw_REG_SOFT_RESET_UNHOLD_MASK 0xF000000000000000ULL /* Mask to handle unhold request */
+
+/* Audio channel control defines */
+#define chipcHw_REG_AUDIO_CHANNEL_ENABLE_ALL 0x00000001 /* Enable all audio channel */
+#define chipcHw_REG_AUDIO_CHANNEL_ENABLE_A 0x00000002 /* Enable channel A */
+#define chipcHw_REG_AUDIO_CHANNEL_ENABLE_B 0x00000004 /* Enable channel B */
+#define chipcHw_REG_AUDIO_CHANNEL_ENABLE_C 0x00000008 /* Enable channel C */
+#define chipcHw_REG_AUDIO_CHANNEL_ENABLE_NTP_CLOCK 0x00000010 /* Enable NTP clock */
+#define chipcHw_REG_AUDIO_CHANNEL_ENABLE_PCM0_CLOCK 0x00000020 /* Enable PCM0 clock */
+#define chipcHw_REG_AUDIO_CHANNEL_ENABLE_PCM1_CLOCK 0x00000040 /* Enable PCM1 clock */
+#define chipcHw_REG_AUDIO_CHANNEL_ENABLE_APM_CLOCK 0x00000080 /* Enable APM clock */
+
+/* Misc. chip control defines */
+#define chipcHw_REG_MISC_CTRL_GE_SEL 0x00040000 /* Select GE2/GE3 */
+#define chipcHw_REG_MISC_CTRL_I2S1_CLOCK_ONCHIP 0x00000000 /* Use on chip clock for I2S1 */
+#define chipcHw_REG_MISC_CTRL_I2S1_CLOCK_GPIO 0x00020000 /* Use external clock via GPIO pin 26 for I2S1 */
+#define chipcHw_REG_MISC_CTRL_I2S0_CLOCK_ONCHIP 0x00000000 /* Use on chip clock for I2S0 */
+#define chipcHw_REG_MISC_CTRL_I2S0_CLOCK_GPIO 0x00010000 /* Use external clock via GPIO pin 45 for I2S0 */
+#define chipcHw_REG_MISC_CTRL_ARM_CP15_DISABLE 0x00008000 /* Disable ARM CP15 bit */
+#define chipcHw_REG_MISC_CTRL_RTC_DISABLE 0x00000008 /* Disable RTC registers */
+#define chipcHw_REG_MISC_CTRL_BBRAM_DISABLE 0x00000004 /* Disable Battery Backed RAM */
+#define chipcHw_REG_MISC_CTRL_USB_MODE_HOST 0x00000002 /* Set USB as host */
+#define chipcHw_REG_MISC_CTRL_USB_MODE_DEVICE 0xFFFFFFFD /* Set USB as device */
+#define chipcHw_REG_MISC_CTRL_USB_POWERON 0xFFFFFFFE /* Power up USB */
+#define chipcHw_REG_MISC_CTRL_USB_POWEROFF 0x00000001 /* Power down USB */
+
+/* OTP configuration defines */
+#define chipcHw_REG_OTP_SECURITY_OFF 0x0000020000000000ULL /* Security support is OFF */
+#define chipcHw_REG_OTP_SPU_SLOW 0x0000010000000000ULL /* Limited SPU throughput */
+#define chipcHw_REG_OTP_LCD_SPEED 0x0000000600000000ULL /* Set VPM speed one */
+#define chipcHw_REG_OTP_VPM_SPEED_1 0x0000000100000000ULL /* Set VPM speed one */
+#define chipcHw_REG_OTP_VPM_SPEED_0 0x0000000080000000ULL /* Set VPM speed zero */
+#define chipcHw_REG_OTP_AXI_SPEED 0x0000000060000000ULL /* Set maximum AXI bus speed */
+#define chipcHw_REG_OTP_APM_DISABLE 0x000000001F000000ULL /* Disable APM */
+#define chipcHw_REG_OTP_PIF_DISABLE 0x0000000000200000ULL /* Disable PIF */
+#define chipcHw_REG_OTP_VDEC_DISABLE 0x0000000000100000ULL /* Disable Video decoder */
+#define chipcHw_REG_OTP_BBL_DISABLE 0x0000000000080000ULL /* Disable RTC and BBRAM */
+#define chipcHw_REG_OTP_LED_DISABLE 0x0000000000040000ULL /* Disable LED */
+#define chipcHw_REG_OTP_GE_DISABLE 0x0000000000020000ULL /* Disable Graphics Engine */
+#define chipcHw_REG_OTP_LCD_DISABLE 0x0000000000010000ULL /* Disable LCD */
+#define chipcHw_REG_OTP_KEYPAD_DISABLE 0x0000000000008000ULL /* Disable keypad */
+#define chipcHw_REG_OTP_UART_DISABLE 0x0000000000004000ULL /* Disable UART */
+#define chipcHw_REG_OTP_SDIOH_DISABLE 0x0000000000003000ULL /* Disable SDIO host */
+#define chipcHw_REG_OTP_HSS_DISABLE 0x0000000000000C00ULL /* Disable HSS */
+#define chipcHw_REG_OTP_TSC_DISABLE 0x0000000000000200ULL /* Disable touch screen */
+#define chipcHw_REG_OTP_USB_DISABLE 0x0000000000000180ULL /* Disable USB */
+#define chipcHw_REG_OTP_SGMII_DISABLE 0x0000000000000060ULL /* Disable SGMII */
+#define chipcHw_REG_OTP_ETH_DISABLE 0x0000000000000018ULL /* Disable gigabit ethernet */
+#define chipcHw_REG_OTP_ETH_PHY_DISABLE 0x0000000000000006ULL /* Disable ethernet PHY */
+#define chipcHw_REG_OTP_VPM_DISABLE 0x0000000000000001ULL /* Disable VPM */
+
+/* Sticky bit defines */
+#define chipcHw_REG_STICKY_BOOT_DONE 0x00000001 /* Boot done */
+#define chipcHw_REG_STICKY_SOFT_RESET 0x00000002 /* ARM soft reset */
+#define chipcHw_REG_STICKY_GENERAL_1 0x00000004 /* General purpose bit 1 */
+#define chipcHw_REG_STICKY_GENERAL_2 0x00000008 /* General purpose bit 2 */
+#define chipcHw_REG_STICKY_GENERAL_3 0x00000010 /* General purpose bit 3 */
+#define chipcHw_REG_STICKY_GENERAL_4 0x00000020 /* General purpose bit 4 */
+#define chipcHw_REG_STICKY_GENERAL_5 0x00000040 /* General purpose bit 5 */
+#define chipcHw_REG_STICKY_POR_BROM 0x00000080 /* Special sticky bit for security - set in BROM to avoid other modes being entered */
+#define chipcHw_REG_STICKY_ARM_RESET 0x00000100 /* ARM reset */
+#define chipcHw_REG_STICKY_CHIP_SOFT_RESET 0x00000200 /* Chip soft reset */
+#define chipcHw_REG_STICKY_CHIP_WARM_RESET 0x00000400 /* Chip warm reset */
+#define chipcHw_REG_STICKY_WDOG_RESET 0x00000800 /* Watchdog reset */
+#define chipcHw_REG_STICKY_OTP_RESET 0x00001000 /* OTP reset */
+
+ /* HW phase alignment defines *//* Spare1 register definitions */
+#define chipcHw_REG_SPARE1_DDR_PHASE_INTR_ENABLE 0x80000000 /* Enable DDR phase align panic interrupt */
+#define chipcHw_REG_SPARE1_VPM_PHASE_INTR_ENABLE 0x40000000 /* Enable VPM phase align panic interrupt */
+#define chipcHw_REG_SPARE1_VPM_BUS_ACCESS_ENABLE 0x00000002 /* Enable access to VPM using system BUS */
+#define chipcHw_REG_SPARE1_DDR_BUS_ACCESS_ENABLE 0x00000001 /* Enable access to DDR using system BUS */
+ /* DDRPhaseCtrl1 register definitions */
+#define chipcHw_REG_DDR_SW_PHASE_CTRL_ENABLE 0x80000000 /* Enable DDR SW phase alignment */
+#define chipcHw_REG_DDR_HW_PHASE_CTRL_ENABLE 0x40000000 /* Enable DDR HW phase alignment */
+#define chipcHw_REG_DDR_PHASE_VALUE_GE_MASK 0x0000007F /* DDR lower threshold for phase alignment */
+#define chipcHw_REG_DDR_PHASE_VALUE_GE_SHIFT 23
+#define chipcHw_REG_DDR_PHASE_VALUE_LE_MASK 0x0000007F /* DDR upper threshold for phase alignment */
+#define chipcHw_REG_DDR_PHASE_VALUE_LE_SHIFT 16
+#define chipcHw_REG_DDR_PHASE_ALIGN_WAIT_CYCLE_MASK 0x0000FFFF /* BUS Cycle to wait to run next DDR phase alignment */
+#define chipcHw_REG_DDR_PHASE_ALIGN_WAIT_CYCLE_SHIFT 0
+ /* VPMPhaseCtrl1 register definitions */
+#define chipcHw_REG_VPM_SW_PHASE_CTRL_ENABLE 0x80000000 /* Enable VPM SW phase alignment */
+#define chipcHw_REG_VPM_HW_PHASE_CTRL_ENABLE 0x40000000 /* Enable VPM HW phase alignment */
+#define chipcHw_REG_VPM_PHASE_VALUE_GE_MASK 0x0000007F /* VPM lower threshold for phase alignment */
+#define chipcHw_REG_VPM_PHASE_VALUE_GE_SHIFT 23
+#define chipcHw_REG_VPM_PHASE_VALUE_LE_MASK 0x0000007F /* VPM upper threshold for phase alignment */
+#define chipcHw_REG_VPM_PHASE_VALUE_LE_SHIFT 16
+#define chipcHw_REG_VPM_PHASE_ALIGN_WAIT_CYCLE_MASK 0x0000FFFF /* BUS Cycle to wait to complete the VPM phase alignment */
+#define chipcHw_REG_VPM_PHASE_ALIGN_WAIT_CYCLE_SHIFT 0
+ /* PhaseAlignStatus register definitions */
+#define chipcHw_REG_DDR_TIMEOUT_INTR_STATUS 0x80000000 /* DDR time out interrupt status */
+#define chipcHw_REG_DDR_PHASE_STATUS_MASK 0x0000007F /* DDR phase status value */
+#define chipcHw_REG_DDR_PHASE_STATUS_SHIFT 24
+#define chipcHw_REG_DDR_PHASE_ALIGNED 0x00800000 /* DDR Phase aligned status */
+#define chipcHw_REG_DDR_LOAD 0x00400000 /* Load DDR phase status */
+#define chipcHw_REG_DDR_PHASE_CTRL_MASK 0x0000003F /* DDR phase control value */
+#define chipcHw_REG_DDR_PHASE_CTRL_SHIFT 16
+#define chipcHw_REG_VPM_TIMEOUT_INTR_STATUS 0x80000000 /* VPM time out interrupt status */
+#define chipcHw_REG_VPM_PHASE_STATUS_MASK 0x0000007F /* VPM phase status value */
+#define chipcHw_REG_VPM_PHASE_STATUS_SHIFT 8
+#define chipcHw_REG_VPM_PHASE_ALIGNED 0x00000080 /* VPM Phase aligned status */
+#define chipcHw_REG_VPM_LOAD 0x00000040 /* Load VPM phase status */
+#define chipcHw_REG_VPM_PHASE_CTRL_MASK 0x0000003F /* VPM phase control value */
+#define chipcHw_REG_VPM_PHASE_CTRL_SHIFT 0
+ /* DDRPhaseCtrl2 register definitions */
+#define chipcHw_REG_DDR_INTR_SERVICED 0x02000000 /* Acknowledge that interrupt was serviced */
+#define chipcHw_REG_DDR_TIMEOUT_INTR_ENABLE 0x01000000 /* Enable time out interrupt */
+#define chipcHw_REG_DDR_LOAD_COUNT_PHASE_CTRL_MASK 0x0000000F /* Wait before toggling load_ch */
+#define chipcHw_REG_DDR_LOAD_COUNT_PHASE_CTRL_SHIFT 20
+#define chipcHw_REG_DDR_TOTAL_LOAD_COUNT_CTRL_MASK 0x0000000F /* Total wait to settle ph_ctrl and load_ch */
+#define chipcHw_REG_DDR_TOTAL_LOAD_COUNT_CTRL_SHIFT 16
+#define chipcHw_REG_DDR_PHASE_TIMEOUT_COUNT_MASK 0x0000FFFF /* Time out value for DDR HW phase alignment */
+#define chipcHw_REG_DDR_PHASE_TIMEOUT_COUNT_SHIFT 0
+ /* VPMPhaseCtrl2 register definitions */
+#define chipcHw_REG_VPM_INTR_SELECT_MASK 0x00000003 /* Interrupt select */
+#define chipcHw_REG_VPM_INTR_SELECT_SHIFT 26
+#define chipcHw_REG_VPM_INTR_DISABLE 0x00000000
+#define chipcHw_REG_VPM_INTR_FAST (0x1 << chipcHw_REG_VPM_INTR_SELECT_SHIFT)
+#define chipcHw_REG_VPM_INTR_MEDIUM (0x2 << chipcHw_REG_VPM_INTR_SELECT_SHIFT)
+#define chipcHw_REG_VPM_INTR_SLOW (0x3 << chipcHw_REG_VPM_INTR_SELECT_SHIFT)
+#define chipcHw_REG_VPM_INTR_SERVICED 0x02000000 /* Acknowledge that interrupt was serviced */
+#define chipcHw_REG_VPM_TIMEOUT_INTR_ENABLE 0x01000000 /* Enable time out interrupt */
+#define chipcHw_REG_VPM_LOAD_COUNT_PHASE_CTRL_MASK 0x0000000F /* Wait before toggling load_ch */
+#define chipcHw_REG_VPM_LOAD_COUNT_PHASE_CTRL_SHIFT 20
+#define chipcHw_REG_VPM_TOTAL_LOAD_COUNT_CTRL_MASK 0x0000000F /* Total wait cycle to settle ph_ctrl and load_ch */
+#define chipcHw_REG_VPM_TOTAL_LOAD_COUNT_CTRL_SHIFT 16
+#define chipcHw_REG_VPM_PHASE_TIMEOUT_COUNT_MASK 0x0000FFFF /* Time out value for VPM HW phase alignment */
+#define chipcHw_REG_VPM_PHASE_TIMEOUT_COUNT_SHIFT 0
+
+#endif /* CHIPCHW_REG_H */
diff --git a/arch/arm/mach-bcmring/include/mach/csp/ddrcReg.h b/arch/arm/mach-bcmring/include/mach/csp/ddrcReg.h
new file mode 100644
index 00000000000..f1b68e26fa6
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/ddrcReg.h
@@ -0,0 +1,872 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file ddrcReg.h
+*
+* @brief Register definitions for BCMRING DDR2 Controller and PHY
+*
+*/
+/****************************************************************************/
+
+#ifndef DDRC_REG_H
+#define DDRC_REG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <csp/reg.h>
+#include <csp/stdint.h>
+
+#include <mach/csp/mm_io.h>
+
+/* ---- Public Constants and Types --------------------------------------- */
+
+/*********************************************************************/
+/* DDR2 Controller (ARM PL341) register definitions */
+/*********************************************************************/
+
+/* -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+/* ARM PL341 DDR2 configuration registers, offset 0x000 */
+/* -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+
+ typedef struct {
+ uint32_t memcStatus;
+ uint32_t memcCmd;
+ uint32_t directCmd;
+ uint32_t memoryCfg;
+ uint32_t refreshPrd;
+ uint32_t casLatency;
+ uint32_t writeLatency;
+ uint32_t tMrd;
+ uint32_t tRas;
+ uint32_t tRc;
+ uint32_t tRcd;
+ uint32_t tRfc;
+ uint32_t tRp;
+ uint32_t tRrd;
+ uint32_t tWr;
+ uint32_t tWtr;
+ uint32_t tXp;
+ uint32_t tXsr;
+ uint32_t tEsr;
+ uint32_t memoryCfg2;
+ uint32_t memoryCfg3;
+ uint32_t tFaw;
+ } ddrcReg_CTLR_MEMC_REG_t;
+
+#define ddrcReg_CTLR_MEMC_REG_OFFSET 0x0000
+#define ddrcReg_CTLR_MEMC_REGP ((volatile ddrcReg_CTLR_MEMC_REG_t *) (MM_IO_BASE_DDRC + ddrcReg_CTLR_MEMC_REG_OFFSET))
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_MEMC_STATUS_BANKS_MASK (0x3 << 12)
+#define ddrcReg_CTLR_MEMC_STATUS_BANKS_4 (0x0 << 12)
+#define ddrcReg_CTLR_MEMC_STATUS_BANKS_8 (0x3 << 12)
+
+#define ddrcReg_CTLR_MEMC_STATUS_MONITORS_MASK (0x3 << 10)
+#define ddrcReg_CTLR_MEMC_STATUS_MONITORS_0 (0x0 << 10)
+#define ddrcReg_CTLR_MEMC_STATUS_MONITORS_1 (0x1 << 10)
+#define ddrcReg_CTLR_MEMC_STATUS_MONITORS_2 (0x2 << 10)
+#define ddrcReg_CTLR_MEMC_STATUS_MONITORS_4 (0x3 << 10)
+
+#define ddrcReg_CTLR_MEMC_STATUS_CHIPS_MASK (0x3 << 7)
+#define ddrcReg_CTLR_MEMC_STATUS_CHIPS_1 (0x0 << 7)
+#define ddrcReg_CTLR_MEMC_STATUS_CHIPS_2 (0x1 << 7)
+#define ddrcReg_CTLR_MEMC_STATUS_CHIPS_3 (0x2 << 7)
+#define ddrcReg_CTLR_MEMC_STATUS_CHIPS_4 (0x3 << 7)
+
+#define ddrcReg_CTLR_MEMC_STATUS_TYPE_MASK (0x7 << 4)
+#define ddrcReg_CTLR_MEMC_STATUS_TYPE_DDR2 (0x5 << 4)
+
+#define ddrcReg_CTLR_MEMC_STATUS_WIDTH_MASK (0x3 << 2)
+#define ddrcReg_CTLR_MEMC_STATUS_WIDTH_16 (0x0 << 2)
+#define ddrcReg_CTLR_MEMC_STATUS_WIDTH_32 (0x1 << 2)
+#define ddrcReg_CTLR_MEMC_STATUS_WIDTH_64 (0x2 << 2)
+#define ddrcReg_CTLR_MEMC_STATUS_WIDTH_128 (0x3 << 2)
+
+#define ddrcReg_CTLR_MEMC_STATUS_STATE_MASK (0x3 << 0)
+#define ddrcReg_CTLR_MEMC_STATUS_STATE_CONFIG (0x0 << 0)
+#define ddrcReg_CTLR_MEMC_STATUS_STATE_READY (0x1 << 0)
+#define ddrcReg_CTLR_MEMC_STATUS_STATE_PAUSED (0x2 << 0)
+#define ddrcReg_CTLR_MEMC_STATUS_STATE_LOWPWR (0x3 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_MEMC_CMD_MASK (0x7 << 0)
+#define ddrcReg_CTLR_MEMC_CMD_GO (0x0 << 0)
+#define ddrcReg_CTLR_MEMC_CMD_SLEEP (0x1 << 0)
+#define ddrcReg_CTLR_MEMC_CMD_WAKEUP (0x2 << 0)
+#define ddrcReg_CTLR_MEMC_CMD_PAUSE (0x3 << 0)
+#define ddrcReg_CTLR_MEMC_CMD_CONFIGURE (0x4 << 0)
+#define ddrcReg_CTLR_MEMC_CMD_ACTIVE_PAUSE (0x7 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_DIRECT_CMD_CHIP_SHIFT 20
+#define ddrcReg_CTLR_DIRECT_CMD_CHIP_MASK (0x3 << ddrcReg_CTLR_DIRECT_CMD_CHIP_SHIFT)
+
+#define ddrcReg_CTLR_DIRECT_CMD_TYPE_PRECHARGEALL (0x0 << 18)
+#define ddrcReg_CTLR_DIRECT_CMD_TYPE_AUTOREFRESH (0x1 << 18)
+#define ddrcReg_CTLR_DIRECT_CMD_TYPE_MODEREG (0x2 << 18)
+#define ddrcReg_CTLR_DIRECT_CMD_TYPE_NOP (0x3 << 18)
+
+#define ddrcReg_CTLR_DIRECT_CMD_BANK_SHIFT 16
+#define ddrcReg_CTLR_DIRECT_CMD_BANK_MASK (0x3 << ddrcReg_CTLR_DIRECT_CMD_BANK_SHIFT)
+
+#define ddrcReg_CTLR_DIRECT_CMD_ADDR_SHIFT 0
+#define ddrcReg_CTLR_DIRECT_CMD_ADDR_MASK (0x1ffff << ddrcReg_CTLR_DIRECT_CMD_ADDR_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_MEMORY_CFG_CHIP_CNT_MASK (0x3 << 21)
+#define ddrcReg_CTLR_MEMORY_CFG_CHIP_CNT_1 (0x0 << 21)
+#define ddrcReg_CTLR_MEMORY_CFG_CHIP_CNT_2 (0x1 << 21)
+#define ddrcReg_CTLR_MEMORY_CFG_CHIP_CNT_3 (0x2 << 21)
+#define ddrcReg_CTLR_MEMORY_CFG_CHIP_CNT_4 (0x3 << 21)
+
+#define ddrcReg_CTLR_MEMORY_CFG_QOS_ARID_MASK (0x7 << 18)
+#define ddrcReg_CTLR_MEMORY_CFG_QOS_ARID_3_0 (0x0 << 18)
+#define ddrcReg_CTLR_MEMORY_CFG_QOS_ARID_4_1 (0x1 << 18)
+#define ddrcReg_CTLR_MEMORY_CFG_QOS_ARID_5_2 (0x2 << 18)
+#define ddrcReg_CTLR_MEMORY_CFG_QOS_ARID_6_3 (0x3 << 18)
+#define ddrcReg_CTLR_MEMORY_CFG_QOS_ARID_7_4 (0x4 << 18)
+#define ddrcReg_CTLR_MEMORY_CFG_QOS_ARID_8_5 (0x5 << 18)
+#define ddrcReg_CTLR_MEMORY_CFG_QOS_ARID_9_6 (0x6 << 18)
+#define ddrcReg_CTLR_MEMORY_CFG_QOS_ARID_10_7 (0x7 << 18)
+
+#define ddrcReg_CTLR_MEMORY_CFG_BURST_LEN_MASK (0x7 << 15)
+#define ddrcReg_CTLR_MEMORY_CFG_BURST_LEN_4 (0x2 << 15)
+#define ddrcReg_CTLR_MEMORY_CFG_BURST_LEN_8 (0x3 << 15) /* @note Not supported in PL341 */
+
+#define ddrcReg_CTLR_MEMORY_CFG_PWRDOWN_ENABLE (0x1 << 13)
+
+#define ddrcReg_CTLR_MEMORY_CFG_PWRDOWN_CYCLES_SHIFT 7
+#define ddrcReg_CTLR_MEMORY_CFG_PWRDOWN_CYCLES_MASK (0x3f << ddrcReg_CTLR_MEMORY_CFG_PWRDOWN_CYCLES_SHIFT)
+
+#define ddrcReg_CTLR_MEMORY_CFG_AXI_ROW_BITS_MASK (0x7 << 3)
+#define ddrcReg_CTLR_MEMORY_CFG_AXI_ROW_BITS_11 (0x0 << 3)
+#define ddrcReg_CTLR_MEMORY_CFG_AXI_ROW_BITS_12 (0x1 << 3)
+#define ddrcReg_CTLR_MEMORY_CFG_AXI_ROW_BITS_13 (0x2 << 3)
+#define ddrcReg_CTLR_MEMORY_CFG_AXI_ROW_BITS_14 (0x3 << 3)
+#define ddrcReg_CTLR_MEMORY_CFG_AXI_ROW_BITS_15 (0x4 << 3)
+#define ddrcReg_CTLR_MEMORY_CFG_AXI_ROW_BITS_16 (0x5 << 3)
+
+#define ddrcReg_CTLR_MEMORY_CFG_AXI_COL_BITS_MASK (0x7 << 0)
+#define ddrcReg_CTLR_MEMORY_CFG_AXI_COL_BITS_9 (0x1 << 0)
+#define ddrcReg_CTLR_MEMORY_CFG_AXI_COL_BITS_10 (0x2 << 0)
+#define ddrcReg_CTLR_MEMORY_CFG_AXI_COL_BITS_11 (0x3 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_REFRESH_PRD_SHIFT 0
+#define ddrcReg_CTLR_REFRESH_PRD_MASK (0x7fff << ddrcReg_CTLR_REFRESH_PRD_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_CAS_LATENCY_SHIFT 1
+#define ddrcReg_CTLR_CAS_LATENCY_MASK (0x7 << ddrcReg_CTLR_CAS_LATENCY_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_WRITE_LATENCY_SHIFT 0
+#define ddrcReg_CTLR_WRITE_LATENCY_MASK (0x7 << ddrcReg_CTLR_WRITE_LATENCY_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_MRD_SHIFT 0
+#define ddrcReg_CTLR_T_MRD_MASK (0x7f << ddrcReg_CTLR_T_MRD_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_RAS_SHIFT 0
+#define ddrcReg_CTLR_T_RAS_MASK (0x1f << ddrcReg_CTLR_T_RAS_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_RC_SHIFT 0
+#define ddrcReg_CTLR_T_RC_MASK (0x1f << ddrcReg_CTLR_T_RC_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_RCD_SCHEDULE_DELAY_SHIFT 8
+#define ddrcReg_CTLR_T_RCD_SCHEDULE_DELAY_MASK (0x7 << ddrcReg_CTLR_T_RCD_SCHEDULE_DELAY_SHIFT)
+
+#define ddrcReg_CTLR_T_RCD_SHIFT 0
+#define ddrcReg_CTLR_T_RCD_MASK (0x7 << ddrcReg_CTLR_T_RCD_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_RFC_SCHEDULE_DELAY_SHIFT 8
+#define ddrcReg_CTLR_T_RFC_SCHEDULE_DELAY_MASK (0x7f << ddrcReg_CTLR_T_RFC_SCHEDULE_DELAY_SHIFT)
+
+#define ddrcReg_CTLR_T_RFC_SHIFT 0
+#define ddrcReg_CTLR_T_RFC_MASK (0x7f << ddrcReg_CTLR_T_RFC_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_RP_SCHEDULE_DELAY_SHIFT 8
+#define ddrcReg_CTLR_T_RP_SCHEDULE_DELAY_MASK (0x7 << ddrcReg_CTLR_T_RP_SCHEDULE_DELAY_SHIFT)
+
+#define ddrcReg_CTLR_T_RP_SHIFT 0
+#define ddrcReg_CTLR_T_RP_MASK (0xf << ddrcReg_CTLR_T_RP_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_RRD_SHIFT 0
+#define ddrcReg_CTLR_T_RRD_MASK (0xf << ddrcReg_CTLR_T_RRD_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_WR_SHIFT 0
+#define ddrcReg_CTLR_T_WR_MASK (0x7 << ddrcReg_CTLR_T_WR_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_WTR_SHIFT 0
+#define ddrcReg_CTLR_T_WTR_MASK (0x7 << ddrcReg_CTLR_T_WTR_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_XP_SHIFT 0
+#define ddrcReg_CTLR_T_XP_MASK (0xff << ddrcReg_CTLR_T_XP_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_XSR_SHIFT 0
+#define ddrcReg_CTLR_T_XSR_MASK (0xff << ddrcReg_CTLR_T_XSR_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_ESR_SHIFT 0
+#define ddrcReg_CTLR_T_ESR_MASK (0xff << ddrcReg_CTLR_T_ESR_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_MEMORY_CFG2_WIDTH_MASK (0x3 << 6)
+#define ddrcReg_CTLR_MEMORY_CFG2_WIDTH_16BITS (0 << 6)
+#define ddrcReg_CTLR_MEMORY_CFG2_WIDTH_32BITS (1 << 6)
+#define ddrcReg_CTLR_MEMORY_CFG2_WIDTH_64BITS (2 << 6)
+
+#define ddrcReg_CTLR_MEMORY_CFG2_AXI_BANK_BITS_MASK (0x3 << 4)
+#define ddrcReg_CTLR_MEMORY_CFG2_AXI_BANK_BITS_2 (0 << 4)
+#define ddrcReg_CTLR_MEMORY_CFG2_AXI_BANK_BITS_3 (3 << 4)
+
+#define ddrcReg_CTLR_MEMORY_CFG2_CKE_INIT_STATE_LOW (0 << 3)
+#define ddrcReg_CTLR_MEMORY_CFG2_CKE_INIT_STATE_HIGH (1 << 3)
+
+#define ddrcReg_CTLR_MEMORY_CFG2_DQM_INIT_STATE_LOW (0 << 2)
+#define ddrcReg_CTLR_MEMORY_CFG2_DQM_INIT_STATE_HIGH (1 << 2)
+
+#define ddrcReg_CTLR_MEMORY_CFG2_CLK_MASK (0x3 << 0)
+#define ddrcReg_CTLR_MEMORY_CFG2_CLK_ASYNC (0 << 0)
+#define ddrcReg_CTLR_MEMORY_CFG2_CLK_SYNC_A_LE_M (1 << 0)
+#define ddrcReg_CTLR_MEMORY_CFG2_CLK_SYNC_A_GT_M (3 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_MEMORY_CFG3_REFRESH_TO_SHIFT 0
+#define ddrcReg_CTLR_MEMORY_CFG3_REFRESH_TO_MASK (0x7 << ddrcReg_CTLR_MEMORY_CFG3_REFRESH_TO_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_T_FAW_SCHEDULE_DELAY_SHIFT 8
+#define ddrcReg_CTLR_T_FAW_SCHEDULE_DELAY_MASK (0x1f << ddrcReg_CTLR_T_FAW_SCHEDULE_DELAY_SHIFT)
+
+#define ddrcReg_CTLR_T_FAW_PERIOD_SHIFT 0
+#define ddrcReg_CTLR_T_FAW_PERIOD_MASK (0x1f << ddrcReg_CTLR_T_FAW_PERIOD_SHIFT)
+
+/* -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+/* ARM PL341 AXI ID QOS configuration registers, offset 0x100 */
+/* -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+
+#define ddrcReg_CTLR_QOS_CNT 16
+#define ddrcReg_CTLR_QOS_MAX (ddrcReg_CTLR_QOS_CNT - 1)
+
+ typedef struct {
+ uint32_t cfg[ddrcReg_CTLR_QOS_CNT];
+ } ddrcReg_CTLR_QOS_REG_t;
+
+#define ddrcReg_CTLR_QOS_REG_OFFSET 0x100
+#define ddrcReg_CTLR_QOS_REGP ((volatile ddrcReg_CTLR_QOS_REG_t *) (MM_IO_BASE_DDRC + ddrcReg_CTLR_QOS_REG_OFFSET))
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_QOS_CFG_MAX_SHIFT 2
+#define ddrcReg_CTLR_QOS_CFG_MAX_MASK (0xff << ddrcReg_CTLR_QOS_CFG_MAX_SHIFT)
+
+#define ddrcReg_CTLR_QOS_CFG_MIN_SHIFT 1
+#define ddrcReg_CTLR_QOS_CFG_MIN_MASK (1 << ddrcReg_CTLR_QOS_CFG_MIN_SHIFT)
+
+#define ddrcReg_CTLR_QOS_CFG_ENABLE (1 << 0)
+
+/* -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+/* ARM PL341 Memory chip configuration registers, offset 0x200 */
+/* -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+
+#define ddrcReg_CTLR_CHIP_CNT 4
+#define ddrcReg_CTLR_CHIP_MAX (ddrcReg_CTLR_CHIP_CNT - 1)
+
+ typedef struct {
+ uint32_t cfg[ddrcReg_CTLR_CHIP_CNT];
+ } ddrcReg_CTLR_CHIP_REG_t;
+
+#define ddrcReg_CTLR_CHIP_REG_OFFSET 0x200
+#define ddrcReg_CTLR_CHIP_REGP ((volatile ddrcReg_CTLR_CHIP_REG_t *) (MM_IO_BASE_DDRC + ddrcReg_CTLR_CHIP_REG_OFFSET))
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_CHIP_CFG_MEM_ORG_MASK (1 << 16)
+#define ddrcReg_CTLR_CHIP_CFG_MEM_ORG_ROW_BANK_COL (0 << 16)
+#define ddrcReg_CTLR_CHIP_CFG_MEM_ORG_BANK_ROW_COL (1 << 16)
+
+#define ddrcReg_CTLR_CHIP_CFG_AXI_ADDR_MATCH_SHIFT 8
+#define ddrcReg_CTLR_CHIP_CFG_AXI_ADDR_MATCH_MASK (0xff << ddrcReg_CTLR_CHIP_CFG_AXI_ADDR_MATCH_SHIFT)
+
+#define ddrcReg_CTLR_CHIP_CFG_AXI_ADDR_MASK_SHIFT 0
+#define ddrcReg_CTLR_CHIP_CFG_AXI_ADDR_MASK_MASK (0xff << ddrcReg_CTLR_CHIP_CFG_AXI_ADDR_MASK_SHIFT)
+
+/* -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+/* ARM PL341 User configuration registers, offset 0x300 */
+/* -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+
+#define ddrcReg_CTLR_USER_OUTPUT_CNT 2
+
+ typedef struct {
+ uint32_t input;
+ uint32_t output[ddrcReg_CTLR_USER_OUTPUT_CNT];
+ uint32_t feature;
+ } ddrcReg_CTLR_USER_REG_t;
+
+#define ddrcReg_CTLR_USER_REG_OFFSET 0x300
+#define ddrcReg_CTLR_USER_REGP ((volatile ddrcReg_CTLR_USER_REG_t *) (MM_IO_BASE_DDRC + ddrcReg_CTLR_USER_REG_OFFSET))
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_USER_INPUT_STATUS_SHIFT 0
+#define ddrcReg_CTLR_USER_INPUT_STATUS_MASK (0xff << ddrcReg_CTLR_USER_INPUT_STATUS_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_USER_OUTPUT_CFG_SHIFT 0
+#define ddrcReg_CTLR_USER_OUTPUT_CFG_MASK (0xff << ddrcReg_CTLR_USER_OUTPUT_CFG_SHIFT)
+
+#define ddrcReg_CTLR_USER_OUTPUT_0_CFG_SYNC_BRIDGE_SHIFT 1
+#define ddrcReg_CTLR_USER_OUTPUT_0_CFG_SYNC_BRIDGE_MASK (1 << ddrcReg_CTLR_USER_OUTPUT_0_CFG_SYNC_BRIDGE_SHIFT)
+#define ddrcReg_CTLR_USER_OUTPUT_0_CFG_SYNC_BRIDGE_BP134 (0 << ddrcReg_CTLR_USER_OUTPUT_0_CFG_SYNC_BRIDGE_SHIFT)
+#define ddrcReg_CTLR_USER_OUTPUT_0_CFG_SYNC_BRIDGE_PL301 (1 << ddrcReg_CTLR_USER_OUTPUT_0_CFG_SYNC_BRIDGE_SHIFT)
+#define ddrcReg_CTLR_USER_OUTPUT_0_CFG_SYNC_BRIDGE_REGISTERED ddrcReg_CTLR_USER_OUTPUT_0_CFG_SYNC_BRIDGE_PL301
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_CTLR_FEATURE_WRITE_BLOCK_DISABLE (1 << 2)
+#define ddrcReg_CTLR_FEATURE_EARLY_BURST_RSP_DISABLE (1 << 0)
+
+/*********************************************************************/
+/* Broadcom DDR23 PHY register definitions */
+/*********************************************************************/
+
+/* -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+/* Broadcom DDR23 PHY Address and Control register definitions */
+/* -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+
+ typedef struct {
+ uint32_t revision;
+ uint32_t pmCtl;
+ REG32_RSVD(0x0008, 0x0010);
+ uint32_t pllStatus;
+ uint32_t pllCfg;
+ uint32_t pllPreDiv;
+ uint32_t pllDiv;
+ uint32_t pllCtl1;
+ uint32_t pllCtl2;
+ uint32_t ssCtl;
+ uint32_t ssCfg;
+ uint32_t vdlStatic;
+ uint32_t vdlDynamic;
+ uint32_t padIdle;
+ uint32_t pvtComp;
+ uint32_t padDrive;
+ uint32_t clkRgltrCtl;
+ } ddrcReg_PHY_ADDR_CTL_REG_t;
+
+#define ddrcReg_PHY_ADDR_CTL_REG_OFFSET 0x0400
+#define ddrcReg_PHY_ADDR_CTL_REGP ((volatile ddrcReg_PHY_ADDR_CTL_REG_t *) (MM_IO_BASE_DDRC + ddrcReg_PHY_ADDR_CTL_REG_OFFSET))
+
+/* @todo These SS definitions are duplicates of ones below */
+
+#define ddrcReg_PHY_ADDR_SS_CTRL_ENABLE 0x00000001
+#define ddrcReg_PHY_ADDR_SS_CFG_CYCLE_PER_TICK_MASK 0xFFFF0000
+#define ddrcReg_PHY_ADDR_SS_CFG_CYCLE_PER_TICK_SHIFT 16
+#define ddrcReg_PHY_ADDR_SS_CFG_MIN_CYCLE_PER_TICK 10 /* Higher the value, lower the SS modulation frequency */
+#define ddrcReg_PHY_ADDR_SS_CFG_NDIV_AMPLITUDE_MASK 0x0000FFFF
+#define ddrcReg_PHY_ADDR_SS_CFG_NDIV_AMPLITUDE_SHIFT 0
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_REVISION_MAJOR_SHIFT 8
+#define ddrcReg_PHY_ADDR_CTL_REVISION_MAJOR_MASK (0xff << ddrcReg_PHY_ADDR_CTL_REVISION_MAJOR_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_REVISION_MINOR_SHIFT 0
+#define ddrcReg_PHY_ADDR_CTL_REVISION_MINOR_MASK (0xff << ddrcReg_PHY_ADDR_CTL_REVISION_MINOR_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_CLK_PM_CTL_DDR_CLK_DISABLE (1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_STATUS_LOCKED (1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_DIV2_CLK_RESET (1 << 31)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_TEST_SEL_SHIFT 17
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_TEST_SEL_MASK (0x1f << ddrcReg_PHY_ADDR_CTL_PLL_CFG_TEST_SEL_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_TEST_ENABLE (1 << 16)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_BGAP_ADJ_SHIFT 12
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_BGAP_ADJ_MASK (0xf << ddrcReg_PHY_ADDR_CTL_PLL_CFG_BGAP_ADJ_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_VCO_RNG (1 << 7)
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_CH1_PWRDWN (1 << 6)
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_BYPASS_ENABLE (1 << 5)
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_CLKOUT_ENABLE (1 << 4)
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_D_RESET (1 << 3)
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_A_RESET (1 << 2)
+#define ddrcReg_PHY_ADDR_CTL_PLL_CFG_PWRDWN (1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_DITHER_MFB (1 << 26)
+#define ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_PWRDWN (1 << 25)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_MODE_SHIFT 20
+#define ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_MODE_MASK (0x7 << ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_MODE_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_INT_SHIFT 8
+#define ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_INT_MASK (0x1ff << ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_INT_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_P2_SHIFT 4
+#define ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_P2_MASK (0xf << ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_P2_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_P1_SHIFT 0
+#define ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_P1_MASK (0xf << ddrcReg_PHY_ADDR_CTL_PLL_PRE_DIV_P1_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_DIV_M1_SHIFT 24
+#define ddrcReg_PHY_ADDR_CTL_PLL_DIV_M1_MASK (0xff << ddrcReg_PHY_ADDR_CTL_PLL_DIV_M1_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_DIV_FRAC_SHIFT 0
+#define ddrcReg_PHY_ADDR_CTL_PLL_DIV_FRAC_MASK (0xffffff << ddrcReg_PHY_ADDR_CTL_PLL_DIV_FRAC_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_TESTA_SHIFT 30
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_TESTA_MASK (0x3 << ddrcReg_PHY_ADDR_CTL_PLL_CTL1_TESTA_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_KVCO_XS_SHIFT 27
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_KVCO_XS_MASK (0x7 << ddrcReg_PHY_ADDR_CTL_PLL_CTL1_KVCO_XS_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_KVCO_XF_SHIFT 24
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_KVCO_XF_MASK (0x7 << ddrcReg_PHY_ADDR_CTL_PLL_CTL1_KVCO_XF_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_LPF_BW_SHIFT 22
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_LPF_BW_MASK (0x3 << ddrcReg_PHY_ADDR_CTL_PLL_CTL1_LPF_BW_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_LF_ORDER (0x1 << 21)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_CN_SHIFT 19
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_CN_MASK (0x3 << ddrcReg_PHY_ADDR_CTL_PLL_CTL1_CN_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_RN_SHIFT 17
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_RN_MASK (0x3 << ddrcReg_PHY_ADDR_CTL_PLL_CTL1_RN_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_CP_SHIFT 15
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_CP_MASK (0x3 << ddrcReg_PHY_ADDR_CTL_PLL_CTL1_CP_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_CZ_SHIFT 13
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_CZ_MASK (0x3 << ddrcReg_PHY_ADDR_CTL_PLL_CTL1_CZ_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_RZ_SHIFT 10
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_RZ_MASK (0x7 << ddrcReg_PHY_ADDR_CTL_PLL_CTL1_RZ_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_ICPX_SHIFT 5
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_ICPX_MASK (0x1f << ddrcReg_PHY_ADDR_CTL_PLL_CTL1_ICPX_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_ICP_OFF_SHIFT 0
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL1_ICP_OFF_MASK (0x1f << ddrcReg_PHY_ADDR_CTL_PLL_CTL1_ICP_OFF_SHIFT)
+
+/* ----------------------------------------------------- */
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL2_PTAP_ADJ_SHIFT 4
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL2_PTAP_ADJ_MASK (0x3 << ddrcReg_PHY_ADDR_CTL_PLL_CTL2_PTAP_ADJ_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL2_CTAP_ADJ_SHIFT 2
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL2_CTAP_ADJ_MASK (0x3 << ddrcReg_PHY_ADDR_CTL_PLL_CTL2_CTAP_ADJ_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL2_LOWCUR_ENABLE (0x1 << 1)
+#define ddrcReg_PHY_ADDR_CTL_PLL_CTL2_BIASIN_ENABLE (0x1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_SS_EN_ENABLE (0x1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_SS_CFG_CYC_PER_TICK_SHIFT 16
+#define ddrcReg_PHY_ADDR_CTL_PLL_SS_CFG_CYC_PER_TICK_MASK (0xffff << ddrcReg_PHY_ADDR_CTL_PLL_SS_CFG_CYC_PER_TICK_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PLL_SS_CFG_NDIV_AMP_SHIFT 0
+#define ddrcReg_PHY_ADDR_CTL_PLL_SS_CFG_NDIV_AMP_MASK (0xffff << ddrcReg_PHY_ADDR_CTL_PLL_SS_CFG_NDIV_AMP_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_VDL_STATIC_OVR_FORCE (1 << 20)
+#define ddrcReg_PHY_ADDR_CTL_VDL_STATIC_OVR_ENABLE (1 << 16)
+
+#define ddrcReg_PHY_ADDR_CTL_VDL_STATIC_OVR_FALL_SHIFT 12
+#define ddrcReg_PHY_ADDR_CTL_VDL_STATIC_OVR_FALL_MASK (0x3 << ddrcReg_PHY_ADDR_CTL_VDL_STATIC_OVR_FALL_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_VDL_STATIC_OVR_RISE_SHIFT 8
+#define ddrcReg_PHY_ADDR_CTL_VDL_STATIC_OVR_RISE_MASK (0x3 << ddrcReg_PHY_ADDR_CTL_VDL_STATIC_OVR_RISE_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_VDL_STATIC_OVR_STEP_SHIFT 0
+#define ddrcReg_PHY_ADDR_CTL_VDL_STATIC_OVR_STEP_MASK (0x3f << ddrcReg_PHY_ADDR_CTL_VDL_STATIC_OVR_STEP_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_VDL_DYNAMIC_OVR_ENABLE (1 << 16)
+
+#define ddrcReg_PHY_ADDR_CTL_VDL_DYNAMIC_OVR_FALL_SHIFT 12
+#define ddrcReg_PHY_ADDR_CTL_VDL_DYNAMIC_OVR_FALL_MASK (0x3 << ddrcReg_PHY_ADDR_CTL_VDL_DYNAMIC_OVR_FALL_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_VDL_DYNAMIC_OVR_RISE_SHIFT 8
+#define ddrcReg_PHY_ADDR_CTL_VDL_DYNAMIC_OVR_RISE_MASK (0x3 << ddrcReg_PHY_ADDR_CTL_VDL_DYNAMIC_OVR_RISE_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_VDL_DYNAMIC_OVR_STEP_SHIFT 0
+#define ddrcReg_PHY_ADDR_CTL_VDL_DYNAMIC_OVR_STEP_MASK (0x3f << ddrcReg_PHY_ADDR_CTL_VDL_DYNAMIC_OVR_STEP_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_PAD_IDLE_ENABLE (1u << 31)
+#define ddrcReg_PHY_ADDR_CTL_PAD_IDLE_RXENB_DISABLE (1 << 8)
+#define ddrcReg_PHY_ADDR_CTL_PAD_IDLE_CTL_IDDQ_DISABLE (1 << 6)
+#define ddrcReg_PHY_ADDR_CTL_PAD_IDLE_CTL_REB_DISABLE (1 << 5)
+#define ddrcReg_PHY_ADDR_CTL_PAD_IDLE_CTL_OEB_DISABLE (1 << 4)
+#define ddrcReg_PHY_ADDR_CTL_PAD_IDLE_CKE_IDDQ_DISABLE (1 << 2)
+#define ddrcReg_PHY_ADDR_CTL_PAD_IDLE_CKE_REB_DISABLE (1 << 1)
+#define ddrcReg_PHY_ADDR_CTL_PAD_IDLE_CKE_OEB_DISABLE (1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_PD_DONE (1 << 30)
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_ND_DONE (1 << 29)
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_SAMPLE_DONE (1 << 28)
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_SAMPLE_AUTO_ENABLE (1 << 27)
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_SAMPLE_ENABLE (1 << 26)
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_ADDR_OVR_ENABLE (1 << 25)
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_DQ_OVR_ENABLE (1 << 24)
+
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_PD_SHIFT 20
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_PD_MASK (0xf << ddrcReg_PHY_ADDR_CTL_PVT_COMP_PD_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_ND_SHIFT 16
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_ND_MASK (0xf << ddrcReg_PHY_ADDR_CTL_PVT_COMP_ND_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_ADDR_PD_SHIFT 12
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_ADDR_PD_MASK (0xf << ddrcReg_PHY_ADDR_CTL_PVT_COMP_ADDR_PD_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_ADDR_ND_SHIFT 8
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_ADDR_ND_MASK (0xf << ddrcReg_PHY_ADDR_CTL_PVT_COMP_ADDR_ND_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_DQ_PD_SHIFT 4
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_DQ_PD_MASK (0xf << ddrcReg_PHY_ADDR_CTL_PVT_COMP_DQ_PD_SHIFT)
+
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_DQ_ND_SHIFT 0
+#define ddrcReg_PHY_ADDR_CTL_PVT_COMP_DQ_ND_MASK (0xf << ddrcReg_PHY_ADDR_CTL_PVT_COMP_DQ_ND_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_PAD_DRIVE_RT60B (1 << 4)
+#define ddrcReg_PHY_ADDR_CTL_PAD_DRIVE_SEL_SSTL18 (1 << 3)
+#define ddrcReg_PHY_ADDR_CTL_PAD_DRIVE_SELTXDRV_CI (1 << 2)
+#define ddrcReg_PHY_ADDR_CTL_PAD_DRIVE_SELRXDRV (1 << 1)
+#define ddrcReg_PHY_ADDR_CTL_PAD_DRIVE_SLEW (1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_ADDR_CTL_CLK_RGLTR_CTL_PWR_HALF (1 << 1)
+#define ddrcReg_PHY_ADDR_CTL_CLK_RGLTR_CTL_PWR_OFF (1 << 0)
+
+/* -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+/* Broadcom DDR23 PHY Byte Lane register definitions */
+/* -------------------------------------------------------------------- */
+/* -------------------------------------------------------------------- */
+
+#define ddrcReg_PHY_BYTE_LANE_CNT 2
+#define ddrcReg_PHY_BYTE_LANE_MAX (ddrcReg_CTLR_BYTE_LANE_CNT - 1)
+
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_CNT 8
+
+ typedef struct {
+ uint32_t revision;
+ uint32_t vdlCalibrate;
+ uint32_t vdlStatus;
+ REG32_RSVD(0x000c, 0x0010);
+ uint32_t vdlOverride[ddrcReg_PHY_BYTE_LANE_VDL_OVR_CNT];
+ uint32_t readCtl;
+ uint32_t readStatus;
+ uint32_t readClear;
+ uint32_t padIdleCtl;
+ uint32_t padDriveCtl;
+ uint32_t padClkCtl;
+ uint32_t writeCtl;
+ uint32_t clkRegCtl;
+ } ddrcReg_PHY_BYTE_LANE_REG_t;
+
+/* There are 2 instances of the byte Lane registers, one for each byte lane. */
+#define ddrcReg_PHY_BYTE_LANE_1_REG_OFFSET 0x0500
+#define ddrcReg_PHY_BYTE_LANE_2_REG_OFFSET 0x0600
+
+#define ddrcReg_PHY_BYTE_LANE_1_REGP ((volatile ddrcReg_PHY_BYTE_LANE_REG_t *) (MM_IO_BASE_DDRC + ddrcReg_PHY_BYTE_LANE_1_REG_OFFSET))
+#define ddrcReg_PHY_BYTE_LANE_2_REGP ((volatile ddrcReg_PHY_BYTE_LANE_REG_t *) (MM_IO_BASE_DDRC + ddrcReg_PHY_BYTE_LANE_2_REG_OFFSET))
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_BYTE_LANE_REVISION_MAJOR_SHIFT 8
+#define ddrcReg_PHY_BYTE_LANE_REVISION_MAJOR_MASK (0xff << ddrcReg_PHY_BYTE_LANE_REVISION_MAJOR_SHIFT)
+
+#define ddrcReg_PHY_BYTE_LANE_REVISION_MINOR_SHIFT 0
+#define ddrcReg_PHY_BYTE_LANE_REVISION_MINOR_MASK (0xff << ddrcReg_PHY_BYTE_LANE_REVISION_MINOR_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_BYTE_LANE_VDL_CALIB_CLK_2CYCLE (1 << 4)
+#define ddrcReg_PHY_BYTE_LANE_VDL_CALIB_CLK_1CYCLE (0 << 4)
+
+#define ddrcReg_PHY_BYTE_LANE_VDL_CALIB_TEST (1 << 3)
+#define ddrcReg_PHY_BYTE_LANE_VDL_CALIB_ALWAYS (1 << 2)
+#define ddrcReg_PHY_BYTE_LANE_VDL_CALIB_ONCE (1 << 1)
+#define ddrcReg_PHY_BYTE_LANE_VDL_CALIB_FAST (1 << 0)
+
+/* ----------------------------------------------------- */
+
+/* The byte lane VDL status calibTotal[9:0] is comprised of [9:4] step value, [3:2] fine fall */
+/* and [1:0] fine rise. Note that calibTotal[9:0] is located at bit 4 in the VDL status */
+/* register. The fine rise and fall are no longer used, so add some definitions for just */
+/* the step setting to simplify things. */
+
+#define ddrcReg_PHY_BYTE_LANE_VDL_STATUS_STEP_SHIFT 8
+#define ddrcReg_PHY_BYTE_LANE_VDL_STATUS_STEP_MASK (0x3f << ddrcReg_PHY_BYTE_LANE_VDL_STATUS_STEP_SHIFT)
+
+#define ddrcReg_PHY_BYTE_LANE_VDL_STATUS_TOTAL_SHIFT 4
+#define ddrcReg_PHY_BYTE_LANE_VDL_STATUS_TOTAL_MASK (0x3ff << ddrcReg_PHY_BYTE_LANE_VDL_STATUS_TOTAL_SHIFT)
+
+#define ddrcReg_PHY_BYTE_LANE_VDL_STATUS_LOCK (1 << 1)
+#define ddrcReg_PHY_BYTE_LANE_VDL_STATUS_IDLE (1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_ENABLE (1 << 16)
+
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_FALL_SHIFT 12
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_FALL_MASK (0x3 << ddrcReg_PHY_BYTE_LANE_VDL_OVR_FALL_SHIFT)
+
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_RISE_SHIFT 8
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_RISE_MASK (0x3 << ddrcReg_PHY_BYTE_LANE_VDL_OVR_RISE_SHIFT)
+
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_STEP_SHIFT 0
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_STEP_MASK (0x3f << ddrcReg_PHY_BYTE_LANE_VDL_OVR_STEP_SHIFT)
+
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_IDX_STATIC_READ_DQS_P 0
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_IDX_STATIC_READ_DQS_N 1
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_IDX_STATIC_READ_EN 2
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_IDX_STATIC_WRITE_DQ_DQM 3
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_IDX_DYNAMIC_READ_DQS_P 4
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_IDX_DYNAMIC_READ_DQS_N 5
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_IDX_DYNAMIC_READ_EN 6
+#define ddrcReg_PHY_BYTE_LANE_VDL_OVR_IDX_DYNAMIC_WRITE_DQ_DQM 7
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_BYTE_LANE_READ_CTL_DELAY_SHIFT 8
+#define ddrcReg_PHY_BYTE_LANE_READ_CTL_DELAY_MASK (0x3 << ddrcReg_PHY_BYTE_LANE_READ_CTL_DELAY_SHIFT)
+
+#define ddrcReg_PHY_BYTE_LANE_READ_CTL_DQ_ODT_ENABLE (1 << 3)
+#define ddrcReg_PHY_BYTE_LANE_READ_CTL_DQ_ODT_ADJUST (1 << 2)
+#define ddrcReg_PHY_BYTE_LANE_READ_CTL_RD_ODT_ENABLE (1 << 1)
+#define ddrcReg_PHY_BYTE_LANE_READ_CTL_RD_ODT_ADJUST (1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_BYTE_LANE_READ_STATUS_ERROR_SHIFT 0
+#define ddrcReg_PHY_BYTE_LANE_READ_STATUS_ERROR_MASK (0xf << ddrcReg_PHY_BYTE_LANE_READ_STATUS_ERROR_SHIFT)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_BYTE_LANE_READ_CLEAR_STATUS (1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_ENABLE (1u << 31)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_DM_RXENB_DISABLE (1 << 19)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_DM_IDDQ_DISABLE (1 << 18)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_DM_REB_DISABLE (1 << 17)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_DM_OEB_DISABLE (1 << 16)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_DQ_RXENB_DISABLE (1 << 15)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_DQ_IDDQ_DISABLE (1 << 14)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_DQ_REB_DISABLE (1 << 13)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_DQ_OEB_DISABLE (1 << 12)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_READ_ENB_RXENB_DISABLE (1 << 11)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_READ_ENB_IDDQ_DISABLE (1 << 10)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_READ_ENB_REB_DISABLE (1 << 9)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_READ_ENB_OEB_DISABLE (1 << 8)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_DQS_RXENB_DISABLE (1 << 7)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_DQS_IDDQ_DISABLE (1 << 6)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_DQS_REB_DISABLE (1 << 5)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_DQS_OEB_DISABLE (1 << 4)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_CLK_RXENB_DISABLE (1 << 3)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_CLK_IDDQ_DISABLE (1 << 2)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_CLK_REB_DISABLE (1 << 1)
+#define ddrcReg_PHY_BYTE_LANE_PAD_IDLE_CTL_CLK_OEB_DISABLE (1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_BYTE_LANE_PAD_DRIVE_CTL_RT60B_DDR_READ_ENB (1 << 5)
+#define ddrcReg_PHY_BYTE_LANE_PAD_DRIVE_CTL_RT60B (1 << 4)
+#define ddrcReg_PHY_BYTE_LANE_PAD_DRIVE_CTL_SEL_SSTL18 (1 << 3)
+#define ddrcReg_PHY_BYTE_LANE_PAD_DRIVE_CTL_SELTXDRV_CI (1 << 2)
+#define ddrcReg_PHY_BYTE_LANE_PAD_DRIVE_CTL_SELRXDRV (1 << 1)
+#define ddrcReg_PHY_BYTE_LANE_PAD_DRIVE_CTL_SLEW (1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_BYTE_LANE_PAD_CLK_CTL_DISABLE (1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_BYTE_LANE_WRITE_CTL_PREAMBLE_DDR3 (1 << 0)
+
+/* ----------------------------------------------------- */
+
+#define ddrcReg_PHY_BYTE_LANE_CLK_REG_CTL_PWR_HALF (1 << 1)
+#define ddrcReg_PHY_BYTE_LANE_CLK_REG_CTL_PWR_OFF (1 << 0)
+
+/*********************************************************************/
+/* ARM PL341 DDRC to Broadcom DDR23 PHY glue register definitions */
+/*********************************************************************/
+
+ typedef struct {
+ uint32_t cfg;
+ uint32_t actMonCnt;
+ uint32_t ctl;
+ uint32_t lbistCtl;
+ uint32_t lbistSeed;
+ uint32_t lbistStatus;
+ uint32_t tieOff;
+ uint32_t actMonClear;
+ uint32_t status;
+ uint32_t user;
+ } ddrcReg_CTLR_PHY_GLUE_REG_t;
+
+#define ddrcReg_CTLR_PHY_GLUE_OFFSET 0x0700
+#define ddrcReg_CTLR_PHY_GLUE_REGP ((volatile ddrcReg_CTLR_PHY_GLUE_REG_t *) (MM_IO_BASE_DDRC + ddrcReg_CTLR_PHY_GLUE_OFFSET))
+
+/* ----------------------------------------------------- */
+
+/* DDR2 / AXI block phase alignment interrupt control */
+#define ddrcReg_CTLR_PHY_GLUE_CFG_INT_SHIFT 18
+#define ddrcReg_CTLR_PHY_GLUE_CFG_INT_MASK (0x3 << ddrcReg_CTLR_PHY_GLUE_CFG_INT_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_INT_OFF (0 << ddrcReg_CTLR_PHY_GLUE_CFG_INT_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_INT_ON_TIGHT (1 << ddrcReg_CTLR_PHY_GLUE_CFG_INT_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_INT_ON_MEDIUM (2 << ddrcReg_CTLR_PHY_GLUE_CFG_INT_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_INT_ON_LOOSE (3 << ddrcReg_CTLR_PHY_GLUE_CFG_INT_SHIFT)
+
+#define ddrcReg_CTLR_PHY_GLUE_CFG_PLL_REFCLK_SHIFT 17
+#define ddrcReg_CTLR_PHY_GLUE_CFG_PLL_REFCLK_MASK (1 << ddrcReg_CTLR_PHY_GLUE_CFG_PLL_REFCLK_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_PLL_REFCLK_DIFFERENTIAL (0 << ddrcReg_CTLR_PHY_GLUE_CFG_PLL_REFCLK_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_PLL_REFCLK_CMOS (1 << ddrcReg_CTLR_PHY_GLUE_CFG_PLL_REFCLK_SHIFT)
+
+#define ddrcReg_CTLR_PHY_GLUE_CFG_DIV2CLK_TREE_SHIFT 16
+#define ddrcReg_CTLR_PHY_GLUE_CFG_DIV2CLK_TREE_MASK (1 << ddrcReg_CTLR_PHY_GLUE_CFG_DIV2CLK_TREE_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_DIV2CLK_TREE_DEEP (0 << ddrcReg_CTLR_PHY_GLUE_CFG_DIV2CLK_TREE_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_DIV2CLK_TREE_SHALLOW (1 << ddrcReg_CTLR_PHY_GLUE_CFG_DIV2CLK_TREE_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_HW_FIXED_ALIGNMENT_DISABLED ddrcReg_CTLR_PHY_GLUE_CFG_DIV2CLK_TREE_SHALLOW
+
+#define ddrcReg_CTLR_PHY_GLUE_CFG_SYNC_BRIDGE_SHIFT 15
+#define ddrcReg_CTLR_PHY_GLUE_CFG_SYNC_BRIDGE_MASK (1 << ddrcReg_CTLR_PHY_GLUE_CFG_SYNC_BRIDGE_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_SYNC_BRIDGE_BP134 (0 << ddrcReg_CTLR_PHY_GLUE_CFG_SYNC_BRIDGE_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_SYNC_BRIDGE_PL301 (1 << ddrcReg_CTLR_PHY_GLUE_CFG_SYNC_BRIDGE_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_SYNC_BRIDGE_REGISTERED ddrcReg_CTLR_PHY_GLUE_CFG_SYNC_BRIDGE_PL301
+
+/* Software control of PHY VDL updates from control register settings. Bit 13 enables the use of Bit 14. */
+/* If software control is not enabled, then updates occur when a refresh command is issued by the hardware */
+/* controller. If 2 chips selects are being used, then software control must be enabled. */
+#define ddrcReg_CTLR_PHY_GLUE_CFG_PHY_VDL_UPDATE_SW_CTL_LOAD (1 << 14)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_PHY_VDL_UPDATE_SW_CTL_ENABLE (1 << 13)
+
+/* Use these to bypass a pipeline stage. By default the ADDR is off but the BYTE LANE in / out are on. */
+#define ddrcReg_CTLR_PHY_GLUE_CFG_PHY_ADDR_CTL_IN_BYPASS_PIPELINE_STAGE (1 << 12)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_PHY_BYTE_LANE_IN_BYPASS_PIPELINE_STAGE (1 << 11)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_PHY_BYTE_LANE_OUT_BYPASS_PIPELINE_STAGE (1 << 10)
+
+/* Chip select count */
+#define ddrcReg_CTLR_PHY_GLUE_CFG_CS_CNT_SHIFT 9
+#define ddrcReg_CTLR_PHY_GLUE_CFG_CS_CNT_MASK (1 << ddrcReg_CTLR_PHY_GLUE_CFG_CS_CNT_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_CS_CNT_1 (0 << ddrcReg_CTLR_PHY_GLUE_CFG_CS_CNT_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_CS_CNT_2 (1 << ddrcReg_CTLR_PHY_GLUE_CFG_CS_CNT_SHIFT)
+
+#define ddrcReg_CTLR_PHY_GLUE_CFG_CLK_SHIFT 8
+#define ddrcReg_CTLR_PHY_GLUE_CFG_CLK_ASYNC (0 << ddrcReg_CTLR_PHY_GLUE_CFG_CLK_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_CLK_SYNC (1 << ddrcReg_CTLR_PHY_GLUE_CFG_CLK_SHIFT)
+
+#define ddrcReg_CTLR_PHY_GLUE_CFG_CKE_INIT_SHIFT 7
+#define ddrcReg_CTLR_PHY_GLUE_CFG_CKE_INIT_LOW (0 << ddrcReg_CTLR_PHY_GLUE_CFG_CKE_INIT_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_CKE_INIT_HIGH (1 << ddrcReg_CTLR_PHY_GLUE_CFG_CKE_INIT_SHIFT)
+
+#define ddrcReg_CTLR_PHY_GLUE_CFG_DQM_INIT_SHIFT 6
+#define ddrcReg_CTLR_PHY_GLUE_CFG_DQM_INIT_LOW (0 << ddrcReg_CTLR_PHY_GLUE_CFG_DQM_INIT_SHIFT)
+#define ddrcReg_CTLR_PHY_GLUE_CFG_DQM_INIT_HIGH (1 << ddrcReg_CTLR_PHY_GLUE_CFG_DQM_INIT_SHIFT)
+
+#define ddrcReg_CTLR_PHY_GLUE_CFG_CAS_LATENCY_SHIFT 0
+#define ddrcReg_CTLR_PHY_GLUE_CFG_CAS_LATENCY_MASK (0x7 << ddrcReg_CTLR_PHY_GLUE_CFG_CAS_LATENCY_SHIFT)
+
+/* ----------------------------------------------------- */
+#define ddrcReg_CTLR_PHY_GLUE_STATUS_PHASE_SHIFT 0
+#define ddrcReg_CTLR_PHY_GLUE_STATUS_PHASE_MASK (0x7f << ddrcReg_CTLR_PHY_GLUE_STATUS_PHASE_SHIFT)
+
+/* ---- Public Function Prototypes --------------------------------------- */
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+#endif /* DDRC_REG_H */
diff --git a/arch/arm/mach-bcmring/include/mach/csp/dmacHw_priv.h b/arch/arm/mach-bcmring/include/mach/csp/dmacHw_priv.h
new file mode 100644
index 00000000000..375066ad018
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/dmacHw_priv.h
@@ -0,0 +1,145 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file dmacHw_priv.h
+*
+* @brief Private Definitions for low level DMA driver
+*
+*/
+/****************************************************************************/
+
+#ifndef _DMACHW_PRIV_H
+#define _DMACHW_PRIV_H
+
+#include <csp/stdint.h>
+
+/* Data type for DMA Link List Item */
+typedef struct {
+ uint32_t sar; /* Source Adress Register.
+ Address must be aligned to CTLx.SRC_TR_WIDTH. */
+ uint32_t dar; /* Destination Address Register.
+ Address must be aligned to CTLx.DST_TR_WIDTH. */
+ uint32_t llpPhy; /* LLP contains the physical address of the next descriptor for block chaining using linked lists.
+ Address MUST be aligned to a 32-bit boundary. */
+ dmacHw_REG64_t ctl; /* Control Register. 64 bits */
+ uint32_t sstat; /* Source Status Register */
+ uint32_t dstat; /* Destination Status Register */
+ uint32_t devCtl; /* Device specific control information */
+ uint32_t llp; /* LLP contains the virtual address of the next descriptor for block chaining using linked lists. */
+} dmacHw_DESC_t;
+
+/*
+ * Descriptor ring pointers
+ */
+typedef struct {
+ int num; /* Number of link items */
+ dmacHw_DESC_t *pHead; /* Head of descriptor ring (for writing) */
+ dmacHw_DESC_t *pTail; /* Tail of descriptor ring (for reading) */
+ dmacHw_DESC_t *pProg; /* Descriptor to program the channel (for programming the channel register) */
+ dmacHw_DESC_t *pEnd; /* End of current descriptor chain */
+ dmacHw_DESC_t *pFree; /* Descriptor to free memory (freeing dynamic memory) */
+ uint32_t virt2PhyOffset; /* Virtual to physical address offset for the descriptor ring */
+} dmacHw_DESC_RING_t;
+
+/*
+ * DMA channel control block
+ */
+typedef struct {
+ uint32_t module; /* DMA controller module (0-1) */
+ uint32_t channel; /* DMA channel (0-7) */
+ volatile uint32_t varDataStarted; /* Flag indicating variable data channel is enabled */
+ volatile uint32_t descUpdated; /* Flag to indicate descriptor update is complete */
+ void *userData; /* Channel specifc user data */
+} dmacHw_CBLK_t;
+
+#define dmacHw_ASSERT(a) if (!(a)) while (1)
+#define dmacHw_MAX_CHANNEL_COUNT 16
+#define dmacHw_FREE_USER_MEMORY 0xFFFFFFFF
+#define dmacHw_DESC_FREE dmacHw_REG_CTL_DONE
+#define dmacHw_DESC_INIT ((dmacHw_DESC_t *) 0xFFFFFFFF)
+#define dmacHw_MAX_BLOCKSIZE 4064
+#define dmacHw_GET_DESC_RING(addr) (dmacHw_DESC_RING_t *)(addr)
+#define dmacHw_ADDRESS_MASK(byte) ((byte) - 1)
+#define dmacHw_NEXT_DESC(rp, dp) ((rp)->dp = (dmacHw_DESC_t *)(rp)->dp->llp)
+#define dmacHw_HANDLE_TO_CBLK(handle) ((dmacHw_CBLK_t *) (handle))
+#define dmacHw_CBLK_TO_HANDLE(cblkp) ((dmacHw_HANDLE_t) (cblkp))
+#define dmacHw_DST_IS_MEMORY(tt) (((tt) == dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM) || ((tt) == dmacHw_TRANSFER_TYPE_MEM_TO_MEM)) ? 1 : 0
+
+/****************************************************************************/
+/**
+* @brief Get next available transaction width
+*
+*
+* @return On sucess : Next avail able transaction width
+* On failure : dmacHw_TRANSACTION_WIDTH_8
+*
+* @note
+* None
+*/
+/****************************************************************************/
+static inline dmacHw_TRANSACTION_WIDTH_e dmacHw_GetNextTrWidth(dmacHw_TRANSACTION_WIDTH_e tw /* [ IN ] Current transaction width */
+ ) {
+ if (tw & dmacHw_REG_CTL_SRC_TR_WIDTH_MASK) {
+ return ((tw >> dmacHw_REG_CTL_SRC_TR_WIDTH_SHIFT) -
+ 1) << dmacHw_REG_CTL_SRC_TR_WIDTH_SHIFT;
+ } else if (tw & dmacHw_REG_CTL_DST_TR_WIDTH_MASK) {
+ return ((tw >> dmacHw_REG_CTL_DST_TR_WIDTH_SHIFT) -
+ 1) << dmacHw_REG_CTL_DST_TR_WIDTH_SHIFT;
+ }
+
+ /* Default return */
+ return dmacHw_SRC_TRANSACTION_WIDTH_8;
+}
+
+/****************************************************************************/
+/**
+* @brief Get number of bytes per transaction
+*
+* @return Number of bytes per transaction
+*
+*
+* @note
+* None
+*/
+/****************************************************************************/
+static inline int dmacHw_GetTrWidthInBytes(dmacHw_TRANSACTION_WIDTH_e tw /* [ IN ] Transaction width */
+ ) {
+ int width = 1;
+ switch (tw) {
+ case dmacHw_SRC_TRANSACTION_WIDTH_8:
+ width = 1;
+ break;
+ case dmacHw_SRC_TRANSACTION_WIDTH_16:
+ case dmacHw_DST_TRANSACTION_WIDTH_16:
+ width = 2;
+ break;
+ case dmacHw_SRC_TRANSACTION_WIDTH_32:
+ case dmacHw_DST_TRANSACTION_WIDTH_32:
+ width = 4;
+ break;
+ case dmacHw_SRC_TRANSACTION_WIDTH_64:
+ case dmacHw_DST_TRANSACTION_WIDTH_64:
+ width = 8;
+ break;
+ default:
+ dmacHw_ASSERT(0);
+ }
+
+ /* Default transaction width */
+ return width;
+}
+
+#endif /* _DMACHW_PRIV_H */
diff --git a/arch/arm/mach-bcmring/include/mach/csp/dmacHw_reg.h b/arch/arm/mach-bcmring/include/mach/csp/dmacHw_reg.h
new file mode 100644
index 00000000000..891cea87e33
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/dmacHw_reg.h
@@ -0,0 +1,406 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file dmacHw_reg.h
+*
+* @brief Definitions for low level DMA registers
+*
+*/
+/****************************************************************************/
+
+#ifndef _DMACHW_REG_H
+#define _DMACHW_REG_H
+
+#include <csp/stdint.h>
+#include <mach/csp/mm_io.h>
+
+/* Data type for 64 bit little endian register */
+typedef struct {
+ volatile uint32_t lo; /* Lower 32 bit in little endian mode */
+ volatile uint32_t hi; /* Upper 32 bit in little endian mode */
+} dmacHw_REG64_t;
+
+/* Data type representing DMA channel registers */
+typedef struct {
+ dmacHw_REG64_t ChannelSar; /* Source Adress Register. 64 bits (upper 32 bits are reserved)
+ Address must be aligned to CTLx.SRC_TR_WIDTH.
+ */
+ dmacHw_REG64_t ChannelDar; /* Destination Address Register.64 bits (upper 32 bits are reserved)
+ Address must be aligned to CTLx.DST_TR_WIDTH.
+ */
+ dmacHw_REG64_t ChannelLlp; /* Link List Pointer.64 bits (upper 32 bits are reserved)
+ LLP contains the pointer to the next LLI for block chaining using linked lists.
+ If LLPis set to 0x0, then transfers using linked lists are not enabled.
+ Address MUST be aligned to a 32-bit boundary.
+ */
+ dmacHw_REG64_t ChannelCtl; /* Control Register. 64 bits */
+ dmacHw_REG64_t ChannelSstat; /* Source Status Register */
+ dmacHw_REG64_t ChannelDstat; /* Destination Status Register */
+ dmacHw_REG64_t ChannelSstatAddr; /* Source Status Address Register */
+ dmacHw_REG64_t ChannelDstatAddr; /* Destination Status Address Register */
+ dmacHw_REG64_t ChannelConfig; /* Channel Configuration Register */
+ dmacHw_REG64_t SrcGather; /* Source gather register */
+ dmacHw_REG64_t DstScatter; /* Destination scatter register */
+} dmacHw_CH_REG_t;
+
+/* Data type for RAW interrupt status registers */
+typedef struct {
+ dmacHw_REG64_t RawTfr; /* Raw Status for IntTfr Interrupt */
+ dmacHw_REG64_t RawBlock; /* Raw Status for IntBlock Interrupt */
+ dmacHw_REG64_t RawSrcTran; /* Raw Status for IntSrcTran Interrupt */
+ dmacHw_REG64_t RawDstTran; /* Raw Status for IntDstTran Interrupt */
+ dmacHw_REG64_t RawErr; /* Raw Status for IntErr Interrupt */
+} dmacHw_INT_RAW_t;
+
+/* Data type for interrupt status registers */
+typedef struct {
+ dmacHw_REG64_t StatusTfr; /* Status for IntTfr Interrupt */
+ dmacHw_REG64_t StatusBlock; /* Status for IntBlock Interrupt */
+ dmacHw_REG64_t StatusSrcTran; /* Status for IntSrcTran Interrupt */
+ dmacHw_REG64_t StatusDstTran; /* Status for IntDstTran Interrupt */
+ dmacHw_REG64_t StatusErr; /* Status for IntErr Interrupt */
+} dmacHw_INT_STATUS_t;
+
+/* Data type for interrupt mask registers*/
+typedef struct {
+ dmacHw_REG64_t MaskTfr; /* Mask for IntTfr Interrupt */
+ dmacHw_REG64_t MaskBlock; /* Mask for IntBlock Interrupt */
+ dmacHw_REG64_t MaskSrcTran; /* Mask for IntSrcTran Interrupt */
+ dmacHw_REG64_t MaskDstTran; /* Mask for IntDstTran Interrupt */
+ dmacHw_REG64_t MaskErr; /* Mask for IntErr Interrupt */
+} dmacHw_INT_MASK_t;
+
+/* Data type for interrupt clear registers */
+typedef struct {
+ dmacHw_REG64_t ClearTfr; /* Clear for IntTfr Interrupt */
+ dmacHw_REG64_t ClearBlock; /* Clear for IntBlock Interrupt */
+ dmacHw_REG64_t ClearSrcTran; /* Clear for IntSrcTran Interrupt */
+ dmacHw_REG64_t ClearDstTran; /* Clear for IntDstTran Interrupt */
+ dmacHw_REG64_t ClearErr; /* Clear for IntErr Interrupt */
+ dmacHw_REG64_t StatusInt; /* Status for each interrupt type */
+} dmacHw_INT_CLEAR_t;
+
+/* Data type for software handshaking registers */
+typedef struct {
+ dmacHw_REG64_t ReqSrcReg; /* Source Software Transaction Request Register */
+ dmacHw_REG64_t ReqDstReg; /* Destination Software Transaction Request Register */
+ dmacHw_REG64_t SglReqSrcReg; /* Single Source Transaction Request Register */
+ dmacHw_REG64_t SglReqDstReg; /* Single Destination Transaction Request Register */
+ dmacHw_REG64_t LstSrcReg; /* Last Source Transaction Request Register */
+ dmacHw_REG64_t LstDstReg; /* Last Destination Transaction Request Register */
+} dmacHw_SW_HANDSHAKE_t;
+
+/* Data type for misc. registers */
+typedef struct {
+ dmacHw_REG64_t DmaCfgReg; /* DMA Configuration Register */
+ dmacHw_REG64_t ChEnReg; /* DMA Channel Enable Register */
+ dmacHw_REG64_t DmaIdReg; /* DMA ID Register */
+ dmacHw_REG64_t DmaTestReg; /* DMA Test Register */
+ dmacHw_REG64_t Reserved0; /* Reserved */
+ dmacHw_REG64_t Reserved1; /* Reserved */
+ dmacHw_REG64_t CompParm6; /* Component Parameter 6 */
+ dmacHw_REG64_t CompParm5; /* Component Parameter 5 */
+ dmacHw_REG64_t CompParm4; /* Component Parameter 4 */
+ dmacHw_REG64_t CompParm3; /* Component Parameter 3 */
+ dmacHw_REG64_t CompParm2; /* Component Parameter 2 */
+ dmacHw_REG64_t CompParm1; /* Component Parameter 1 */
+ dmacHw_REG64_t CompId; /* Compoent ID */
+} dmacHw_MISC_t;
+
+/* Base registers */
+#define dmacHw_0_MODULE_BASE_ADDR (char *) MM_IO_BASE_DMA0 /* DMAC 0 module's base address */
+#define dmacHw_1_MODULE_BASE_ADDR (char *) MM_IO_BASE_DMA1 /* DMAC 1 module's base address */
+
+extern uint32_t dmaChannelCount_0;
+extern uint32_t dmaChannelCount_1;
+
+/* Define channel specific registers */
+#define dmacHw_CHAN_BASE(module, chan) ((dmacHw_CH_REG_t *) ((char *)((module) ? dmacHw_1_MODULE_BASE_ADDR : dmacHw_0_MODULE_BASE_ADDR) + ((chan) * sizeof(dmacHw_CH_REG_t))))
+
+/* Raw interrupt status registers */
+#define dmacHw_REG_INT_RAW_BASE(module) ((char *)dmacHw_CHAN_BASE((module), ((module) ? dmaChannelCount_1 : dmaChannelCount_0)))
+#define dmacHw_REG_INT_RAW_TRAN(module) (((dmacHw_INT_RAW_t *) dmacHw_REG_INT_RAW_BASE((module)))->RawTfr.lo)
+#define dmacHw_REG_INT_RAW_BLOCK(module) (((dmacHw_INT_RAW_t *) dmacHw_REG_INT_RAW_BASE((module)))->RawBlock.lo)
+#define dmacHw_REG_INT_RAW_STRAN(module) (((dmacHw_INT_RAW_t *) dmacHw_REG_INT_RAW_BASE((module)))->RawSrcTran.lo)
+#define dmacHw_REG_INT_RAW_DTRAN(module) (((dmacHw_INT_RAW_t *) dmacHw_REG_INT_RAW_BASE((module)))->RawDstTran.lo)
+#define dmacHw_REG_INT_RAW_ERROR(module) (((dmacHw_INT_RAW_t *) dmacHw_REG_INT_RAW_BASE((module)))->RawErr.lo)
+
+/* Interrupt status registers */
+#define dmacHw_REG_INT_STAT_BASE(module) ((char *)(dmacHw_REG_INT_RAW_BASE((module)) + sizeof(dmacHw_INT_RAW_t)))
+#define dmacHw_REG_INT_STAT_TRAN(module) (((dmacHw_INT_STATUS_t *) dmacHw_REG_INT_STAT_BASE((module)))->StatusTfr.lo)
+#define dmacHw_REG_INT_STAT_BLOCK(module) (((dmacHw_INT_STATUS_t *) dmacHw_REG_INT_STAT_BASE((module)))->StatusBlock.lo)
+#define dmacHw_REG_INT_STAT_STRAN(module) (((dmacHw_INT_STATUS_t *) dmacHw_REG_INT_STAT_BASE((module)))->StatusSrcTran.lo)
+#define dmacHw_REG_INT_STAT_DTRAN(module) (((dmacHw_INT_STATUS_t *) dmacHw_REG_INT_STAT_BASE((module)))->StatusDstTran.lo)
+#define dmacHw_REG_INT_STAT_ERROR(module) (((dmacHw_INT_STATUS_t *) dmacHw_REG_INT_STAT_BASE((module)))->StatusErr.lo)
+
+/* Interrupt status registers */
+#define dmacHw_REG_INT_MASK_BASE(module) ((char *)(dmacHw_REG_INT_STAT_BASE((module)) + sizeof(dmacHw_INT_STATUS_t)))
+#define dmacHw_REG_INT_MASK_TRAN(module) (((dmacHw_INT_MASK_t *) dmacHw_REG_INT_MASK_BASE((module)))->MaskTfr.lo)
+#define dmacHw_REG_INT_MASK_BLOCK(module) (((dmacHw_INT_MASK_t *) dmacHw_REG_INT_MASK_BASE((module)))->MaskBlock.lo)
+#define dmacHw_REG_INT_MASK_STRAN(module) (((dmacHw_INT_MASK_t *) dmacHw_REG_INT_MASK_BASE((module)))->MaskSrcTran.lo)
+#define dmacHw_REG_INT_MASK_DTRAN(module) (((dmacHw_INT_MASK_t *) dmacHw_REG_INT_MASK_BASE((module)))->MaskDstTran.lo)
+#define dmacHw_REG_INT_MASK_ERROR(module) (((dmacHw_INT_MASK_t *) dmacHw_REG_INT_MASK_BASE((module)))->MaskErr.lo)
+
+/* Interrupt clear registers */
+#define dmacHw_REG_INT_CLEAR_BASE(module) ((char *)(dmacHw_REG_INT_MASK_BASE((module)) + sizeof(dmacHw_INT_MASK_t)))
+#define dmacHw_REG_INT_CLEAR_TRAN(module) (((dmacHw_INT_CLEAR_t *) dmacHw_REG_INT_CLEAR_BASE((module)))->ClearTfr.lo)
+#define dmacHw_REG_INT_CLEAR_BLOCK(module) (((dmacHw_INT_CLEAR_t *) dmacHw_REG_INT_CLEAR_BASE((module)))->ClearBlock.lo)
+#define dmacHw_REG_INT_CLEAR_STRAN(module) (((dmacHw_INT_CLEAR_t *) dmacHw_REG_INT_CLEAR_BASE((module)))->ClearSrcTran.lo)
+#define dmacHw_REG_INT_CLEAR_DTRAN(module) (((dmacHw_INT_CLEAR_t *) dmacHw_REG_INT_CLEAR_BASE((module)))->ClearDstTran.lo)
+#define dmacHw_REG_INT_CLEAR_ERROR(module) (((dmacHw_INT_CLEAR_t *) dmacHw_REG_INT_CLEAR_BASE((module)))->ClearErr.lo)
+#define dmacHw_REG_INT_STATUS(module) (((dmacHw_INT_CLEAR_t *) dmacHw_REG_INT_CLEAR_BASE((module)))->StatusInt.lo)
+
+/* Software handshaking registers */
+#define dmacHw_REG_SW_HS_BASE(module) ((char *)(dmacHw_REG_INT_CLEAR_BASE((module)) + sizeof(dmacHw_INT_CLEAR_t)))
+#define dmacHw_REG_SW_HS_SRC_REQ(module) (((dmacHw_SW_HANDSHAKE_t *) dmacHw_REG_SW_HS_BASE((module)))->ReqSrcReg.lo)
+#define dmacHw_REG_SW_HS_DST_REQ(module) (((dmacHw_SW_HANDSHAKE_t *) dmacHw_REG_SW_HS_BASE((module)))->ReqDstReg.lo)
+#define dmacHw_REG_SW_HS_SRC_SGL_REQ(module) (((dmacHw_SW_HANDSHAKE_t *) dmacHw_REG_SW_HS_BASE((module)))->SglReqSrcReg.lo)
+#define dmacHw_REG_SW_HS_DST_SGL_REQ(module) (((dmacHw_SW_HANDSHAKE_t *) dmacHw_REG_SW_HS_BASE((module)))->SglReqDstReg.lo)
+#define dmacHw_REG_SW_HS_SRC_LST_REQ(module) (((dmacHw_SW_HANDSHAKE_t *) dmacHw_REG_SW_HS_BASE((module)))->LstSrcReg.lo)
+#define dmacHw_REG_SW_HS_DST_LST_REQ(module) (((dmacHw_SW_HANDSHAKE_t *) dmacHw_REG_SW_HS_BASE((module)))->LstDstReg.lo)
+
+/* Miscellaneous registers */
+#define dmacHw_REG_MISC_BASE(module) ((char *)(dmacHw_REG_SW_HS_BASE((module)) + sizeof(dmacHw_SW_HANDSHAKE_t)))
+#define dmacHw_REG_MISC_CFG(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->DmaCfgReg.lo)
+#define dmacHw_REG_MISC_CH_ENABLE(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->ChEnReg.lo)
+#define dmacHw_REG_MISC_ID(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->DmaIdReg.lo)
+#define dmacHw_REG_MISC_TEST(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->DmaTestReg.lo)
+#define dmacHw_REG_MISC_COMP_PARAM1_LO(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->CompParm1.lo)
+#define dmacHw_REG_MISC_COMP_PARAM1_HI(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->CompParm1.hi)
+#define dmacHw_REG_MISC_COMP_PARAM2_LO(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->CompParm2.lo)
+#define dmacHw_REG_MISC_COMP_PARAM2_HI(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->CompParm2.hi)
+#define dmacHw_REG_MISC_COMP_PARAM3_LO(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->CompParm3.lo)
+#define dmacHw_REG_MISC_COMP_PARAM3_HI(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->CompParm3.hi)
+#define dmacHw_REG_MISC_COMP_PARAM4_LO(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->CompParm4.lo)
+#define dmacHw_REG_MISC_COMP_PARAM4_HI(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->CompParm4.hi)
+#define dmacHw_REG_MISC_COMP_PARAM5_LO(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->CompParm5.lo)
+#define dmacHw_REG_MISC_COMP_PARAM5_HI(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->CompParm5.hi)
+#define dmacHw_REG_MISC_COMP_PARAM6_LO(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->CompParm6.lo)
+#define dmacHw_REG_MISC_COMP_PARAM6_HI(module) (((dmacHw_MISC_t *) dmacHw_REG_MISC_BASE((module)))->CompParm6.hi)
+
+/* Channel control registers */
+#define dmacHw_REG_SAR(module, chan) (dmacHw_CHAN_BASE((module), (chan))->ChannelSar.lo)
+#define dmacHw_REG_DAR(module, chan) (dmacHw_CHAN_BASE((module), (chan))->ChannelDar.lo)
+#define dmacHw_REG_LLP(module, chan) (dmacHw_CHAN_BASE((module), (chan))->ChannelLlp.lo)
+
+#define dmacHw_REG_CTL_LO(module, chan) (dmacHw_CHAN_BASE((module), (chan))->ChannelCtl.lo)
+#define dmacHw_REG_CTL_HI(module, chan) (dmacHw_CHAN_BASE((module), (chan))->ChannelCtl.hi)
+
+#define dmacHw_REG_SSTAT(module, chan) (dmacHw_CHAN_BASE((module), (chan))->ChannelSstat.lo)
+#define dmacHw_REG_DSTAT(module, chan) (dmacHw_CHAN_BASE((module), (chan))->ChannelDstat.lo)
+#define dmacHw_REG_SSTATAR(module, chan) (dmacHw_CHAN_BASE((module), (chan))->ChannelSstatAddr.lo)
+#define dmacHw_REG_DSTATAR(module, chan) (dmacHw_CHAN_BASE((module), (chan))->ChannelDstatAddr.lo)
+
+#define dmacHw_REG_CFG_LO(module, chan) (dmacHw_CHAN_BASE((module), (chan))->ChannelConfig.lo)
+#define dmacHw_REG_CFG_HI(module, chan) (dmacHw_CHAN_BASE((module), (chan))->ChannelConfig.hi)
+
+#define dmacHw_REG_SGR_LO(module, chan) (dmacHw_CHAN_BASE((module), (chan))->SrcGather.lo)
+#define dmacHw_REG_SGR_HI(module, chan) (dmacHw_CHAN_BASE((module), (chan))->SrcGather.hi)
+
+#define dmacHw_REG_DSR_LO(module, chan) (dmacHw_CHAN_BASE((module), (chan))->DstScatter.lo)
+#define dmacHw_REG_DSR_HI(module, chan) (dmacHw_CHAN_BASE((module), (chan))->DstScatter.hi)
+
+#define INT_STATUS_MASK(channel) (0x00000001 << (channel))
+#define CHANNEL_BUSY(mod, channel) (dmacHw_REG_MISC_CH_ENABLE((mod)) & (0x00000001 << (channel)))
+
+/* Bit mask for REG_DMACx_CTL_LO */
+
+#define dmacHw_REG_CTL_INT_EN 0x00000001 /* Channel interrupt enable */
+
+#define dmacHw_REG_CTL_DST_TR_WIDTH_MASK 0x0000000E /* Destination transaction width mask */
+#define dmacHw_REG_CTL_DST_TR_WIDTH_SHIFT 1
+#define dmacHw_REG_CTL_DST_TR_WIDTH_8 0x00000000 /* Destination transaction width 8 bit */
+#define dmacHw_REG_CTL_DST_TR_WIDTH_16 0x00000002 /* Destination transaction width 16 bit */
+#define dmacHw_REG_CTL_DST_TR_WIDTH_32 0x00000004 /* Destination transaction width 32 bit */
+#define dmacHw_REG_CTL_DST_TR_WIDTH_64 0x00000006 /* Destination transaction width 64 bit */
+
+#define dmacHw_REG_CTL_SRC_TR_WIDTH_MASK 0x00000070 /* Source transaction width mask */
+#define dmacHw_REG_CTL_SRC_TR_WIDTH_SHIFT 4
+#define dmacHw_REG_CTL_SRC_TR_WIDTH_8 0x00000000 /* Source transaction width 8 bit */
+#define dmacHw_REG_CTL_SRC_TR_WIDTH_16 0x00000010 /* Source transaction width 16 bit */
+#define dmacHw_REG_CTL_SRC_TR_WIDTH_32 0x00000020 /* Source transaction width 32 bit */
+#define dmacHw_REG_CTL_SRC_TR_WIDTH_64 0x00000030 /* Source transaction width 64 bit */
+
+#define dmacHw_REG_CTL_DS_ENABLE 0x00040000 /* Destination scatter enable */
+#define dmacHw_REG_CTL_SG_ENABLE 0x00020000 /* Source gather enable */
+
+#define dmacHw_REG_CTL_DINC_MASK 0x00000180 /* Destination address inc/dec mask */
+#define dmacHw_REG_CTL_DINC_INC 0x00000000 /* Destination address increment */
+#define dmacHw_REG_CTL_DINC_DEC 0x00000080 /* Destination address decrement */
+#define dmacHw_REG_CTL_DINC_NC 0x00000100 /* Destination address no change */
+
+#define dmacHw_REG_CTL_SINC_MASK 0x00000600 /* Source address inc/dec mask */
+#define dmacHw_REG_CTL_SINC_INC 0x00000000 /* Source address increment */
+#define dmacHw_REG_CTL_SINC_DEC 0x00000200 /* Source address decrement */
+#define dmacHw_REG_CTL_SINC_NC 0x00000400 /* Source address no change */
+
+#define dmacHw_REG_CTL_DST_MSIZE_MASK 0x00003800 /* Destination burst transaction length */
+#define dmacHw_REG_CTL_DST_MSIZE_0 0x00000000 /* No Destination burst */
+#define dmacHw_REG_CTL_DST_MSIZE_4 0x00000800 /* Destination burst transaction length 4 */
+#define dmacHw_REG_CTL_DST_MSIZE_8 0x00001000 /* Destination burst transaction length 8 */
+#define dmacHw_REG_CTL_DST_MSIZE_16 0x00001800 /* Destination burst transaction length 16 */
+
+#define dmacHw_REG_CTL_SRC_MSIZE_MASK 0x0001C000 /* Source burst transaction length */
+#define dmacHw_REG_CTL_SRC_MSIZE_0 0x00000000 /* No Source burst */
+#define dmacHw_REG_CTL_SRC_MSIZE_4 0x00004000 /* Source burst transaction length 4 */
+#define dmacHw_REG_CTL_SRC_MSIZE_8 0x00008000 /* Source burst transaction length 8 */
+#define dmacHw_REG_CTL_SRC_MSIZE_16 0x0000C000 /* Source burst transaction length 16 */
+
+#define dmacHw_REG_CTL_TTFC_MASK 0x00700000 /* Transfer type and flow controller */
+#define dmacHw_REG_CTL_TTFC_MM_DMAC 0x00000000 /* Memory to Memory with DMAC as flow controller */
+#define dmacHw_REG_CTL_TTFC_MP_DMAC 0x00100000 /* Memory to Peripheral with DMAC as flow controller */
+#define dmacHw_REG_CTL_TTFC_PM_DMAC 0x00200000 /* Peripheral to Memory with DMAC as flow controller */
+#define dmacHw_REG_CTL_TTFC_PP_DMAC 0x00300000 /* Peripheral to Peripheral with DMAC as flow controller */
+#define dmacHw_REG_CTL_TTFC_PM_PERI 0x00400000 /* Peripheral to Memory with Peripheral as flow controller */
+#define dmacHw_REG_CTL_TTFC_PP_SPERI 0x00500000 /* Peripheral to Peripheral with Source Peripheral as flow controller */
+#define dmacHw_REG_CTL_TTFC_MP_PERI 0x00600000 /* Memory to Peripheral with Peripheral as flow controller */
+#define dmacHw_REG_CTL_TTFC_PP_DPERI 0x00700000 /* Peripheral to Peripheral with Destination Peripheral as flow controller */
+
+#define dmacHw_REG_CTL_DMS_MASK 0x01800000 /* Destination AHB master interface */
+#define dmacHw_REG_CTL_DMS_1 0x00000000 /* Destination AHB master interface 1 */
+#define dmacHw_REG_CTL_DMS_2 0x00800000 /* Destination AHB master interface 2 */
+
+#define dmacHw_REG_CTL_SMS_MASK 0x06000000 /* Source AHB master interface */
+#define dmacHw_REG_CTL_SMS_1 0x00000000 /* Source AHB master interface 1 */
+#define dmacHw_REG_CTL_SMS_2 0x02000000 /* Source AHB master interface 2 */
+
+#define dmacHw_REG_CTL_LLP_DST_EN 0x08000000 /* Block chaining enable for destination side */
+#define dmacHw_REG_CTL_LLP_SRC_EN 0x10000000 /* Block chaining enable for source side */
+
+/* Bit mask for REG_DMACx_CTL_HI */
+#define dmacHw_REG_CTL_BLOCK_TS_MASK 0x00000FFF /* Block transfer size */
+#define dmacHw_REG_CTL_DONE 0x00001000 /* Block trasnfer done */
+
+/* Bit mask for REG_DMACx_CFG_LO */
+#define dmacHw_REG_CFG_LO_CH_PRIORITY_SHIFT 5 /* Channel priority shift */
+#define dmacHw_REG_CFG_LO_CH_PRIORITY_MASK 0x000000E0 /* Channel priority mask */
+#define dmacHw_REG_CFG_LO_CH_PRIORITY_0 0x00000000 /* Channel priority 0 */
+#define dmacHw_REG_CFG_LO_CH_PRIORITY_1 0x00000020 /* Channel priority 1 */
+#define dmacHw_REG_CFG_LO_CH_PRIORITY_2 0x00000040 /* Channel priority 2 */
+#define dmacHw_REG_CFG_LO_CH_PRIORITY_3 0x00000060 /* Channel priority 3 */
+#define dmacHw_REG_CFG_LO_CH_PRIORITY_4 0x00000080 /* Channel priority 4 */
+#define dmacHw_REG_CFG_LO_CH_PRIORITY_5 0x000000A0 /* Channel priority 5 */
+#define dmacHw_REG_CFG_LO_CH_PRIORITY_6 0x000000C0 /* Channel priority 6 */
+#define dmacHw_REG_CFG_LO_CH_PRIORITY_7 0x000000E0 /* Channel priority 7 */
+
+#define dmacHw_REG_CFG_LO_CH_SUSPEND 0x00000100 /* Channel suspend */
+#define dmacHw_REG_CFG_LO_CH_FIFO_EMPTY 0x00000200 /* Channel FIFO empty */
+#define dmacHw_REG_CFG_LO_DST_CH_SW_HS 0x00000400 /* Destination channel SW handshaking */
+#define dmacHw_REG_CFG_LO_SRC_CH_SW_HS 0x00000800 /* Source channel SW handshaking */
+
+#define dmacHw_REG_CFG_LO_CH_LOCK_MASK 0x00003000 /* Channel locking mask */
+#define dmacHw_REG_CFG_LO_CH_LOCK_DMA 0x00000000 /* Channel lock over the entire DMA transfer operation */
+#define dmacHw_REG_CFG_LO_CH_LOCK_BLOCK 0x00001000 /* Channel lock over the block transfer operation */
+#define dmacHw_REG_CFG_LO_CH_LOCK_TRANS 0x00002000 /* Channel lock over the transaction */
+#define dmacHw_REG_CFG_LO_CH_LOCK_ENABLE 0x00010000 /* Channel lock enable */
+
+#define dmacHw_REG_CFG_LO_BUS_LOCK_MASK 0x0000C000 /* Bus locking mask */
+#define dmacHw_REG_CFG_LO_BUS_LOCK_DMA 0x00000000 /* Bus lock over the entire DMA transfer operation */
+#define dmacHw_REG_CFG_LO_BUS_LOCK_BLOCK 0x00004000 /* Bus lock over the block transfer operation */
+#define dmacHw_REG_CFG_LO_BUS_LOCK_TRANS 0x00008000 /* Bus lock over the transaction */
+#define dmacHw_REG_CFG_LO_BUS_LOCK_ENABLE 0x00020000 /* Bus lock enable */
+
+#define dmacHw_REG_CFG_LO_DST_HS_POLARITY_LOW 0x00040000 /* Destination channel handshaking signal polarity low */
+#define dmacHw_REG_CFG_LO_SRC_HS_POLARITY_LOW 0x00080000 /* Source channel handshaking signal polarity low */
+
+#define dmacHw_REG_CFG_LO_MAX_AMBA_BURST_LEN_MASK 0x3FF00000 /* Maximum AMBA burst length */
+
+#define dmacHw_REG_CFG_LO_AUTO_RELOAD_SRC 0x40000000 /* Source address auto reload */
+#define dmacHw_REG_CFG_LO_AUTO_RELOAD_DST 0x80000000 /* Destination address auto reload */
+
+/* Bit mask for REG_DMACx_CFG_HI */
+#define dmacHw_REG_CFG_HI_FC_DST_READY 0x00000001 /* Source transaction request is serviced when destination is ready */
+#define dmacHw_REG_CFG_HI_FIFO_ENOUGH 0x00000002 /* Initiate burst transaction when enough data in available in FIFO */
+
+#define dmacHw_REG_CFG_HI_AHB_HPROT_MASK 0x0000001C /* AHB protection mask */
+#define dmacHw_REG_CFG_HI_AHB_HPROT_1 0x00000004 /* AHB protection 1 */
+#define dmacHw_REG_CFG_HI_AHB_HPROT_2 0x00000008 /* AHB protection 2 */
+#define dmacHw_REG_CFG_HI_AHB_HPROT_3 0x00000010 /* AHB protection 3 */
+
+#define dmacHw_REG_CFG_HI_UPDATE_DST_STAT 0x00000020 /* Destination status update enable */
+#define dmacHw_REG_CFG_HI_UPDATE_SRC_STAT 0x00000040 /* Source status update enable */
+
+#define dmacHw_REG_CFG_HI_SRC_PERI_INTF_MASK 0x00000780 /* Source peripheral hardware interface mask */
+#define dmacHw_REG_CFG_HI_DST_PERI_INTF_MASK 0x00007800 /* Destination peripheral hardware interface mask */
+
+/* DMA Configuration Parameters */
+#define dmacHw_REG_COMP_PARAM_NUM_CHANNELS 0x00000700 /* Number of channels */
+#define dmacHw_REG_COMP_PARAM_NUM_INTERFACE 0x00001800 /* Number of master interface */
+#define dmacHw_REG_COMP_PARAM_MAX_BLK_SIZE 0x0000000f /* Maximum brust size */
+#define dmacHw_REG_COMP_PARAM_DATA_WIDTH 0x00006000 /* Data transfer width */
+
+/* Define GET/SET macros to program the registers */
+#define dmacHw_SET_SAR(module, channel, addr) (dmacHw_REG_SAR((module), (channel)) = (uint32_t) (addr))
+#define dmacHw_SET_DAR(module, channel, addr) (dmacHw_REG_DAR((module), (channel)) = (uint32_t) (addr))
+#define dmacHw_SET_LLP(module, channel, ptr) (dmacHw_REG_LLP((module), (channel)) = (uint32_t) (ptr))
+
+#define dmacHw_GET_SSTAT(module, channel) (dmacHw_REG_SSTAT((module), (channel)))
+#define dmacHw_GET_DSTAT(module, channel) (dmacHw_REG_DSTAT((module), (channel)))
+
+#define dmacHw_SET_SSTATAR(module, channel, addr) (dmacHw_REG_SSTATAR((module), (channel)) = (uint32_t) (addr))
+#define dmacHw_SET_DSTATAR(module, channel, addr) (dmacHw_REG_DSTATAR((module), (channel)) = (uint32_t) (addr))
+
+#define dmacHw_SET_CONTROL_LO(module, channel, ctl) (dmacHw_REG_CTL_LO((module), (channel)) |= (ctl))
+#define dmacHw_RESET_CONTROL_LO(module, channel) (dmacHw_REG_CTL_LO((module), (channel)) = 0)
+#define dmacHw_GET_CONTROL_LO(module, channel) (dmacHw_REG_CTL_LO((module), (channel)))
+
+#define dmacHw_SET_CONTROL_HI(module, channel, ctl) (dmacHw_REG_CTL_HI((module), (channel)) |= (ctl))
+#define dmacHw_RESET_CONTROL_HI(module, channel) (dmacHw_REG_CTL_HI((module), (channel)) = 0)
+#define dmacHw_GET_CONTROL_HI(module, channel) (dmacHw_REG_CTL_HI((module), (channel)))
+
+#define dmacHw_GET_BLOCK_SIZE(module, channel) (dmacHw_REG_CTL_HI((module), (channel)) & dmacHw_REG_CTL_BLOCK_TS_MASK)
+#define dmacHw_DMA_COMPLETE(module, channel) (dmacHw_REG_CTL_HI((module), (channel)) & dmacHw_REG_CTL_DONE)
+
+#define dmacHw_SET_CONFIG_LO(module, channel, cfg) (dmacHw_REG_CFG_LO((module), (channel)) |= (cfg))
+#define dmacHw_RESET_CONFIG_LO(module, channel) (dmacHw_REG_CFG_LO((module), (channel)) = 0)
+#define dmacHw_GET_CONFIG_LO(module, channel) (dmacHw_REG_CFG_LO((module), (channel)))
+#define dmacHw_SET_AMBA_BUSRT_LEN(module, channel, len) (dmacHw_REG_CFG_LO((module), (channel)) = (dmacHw_REG_CFG_LO((module), (channel)) & ~(dmacHw_REG_CFG_LO_MAX_AMBA_BURST_LEN_MASK)) | (((len) << 20) & dmacHw_REG_CFG_LO_MAX_AMBA_BURST_LEN_MASK))
+#define dmacHw_SET_CHANNEL_PRIORITY(module, channel, prio) (dmacHw_REG_CFG_LO((module), (channel)) = (dmacHw_REG_CFG_LO((module), (channel)) & ~(dmacHw_REG_CFG_LO_CH_PRIORITY_MASK)) | (prio))
+#define dmacHw_SET_AHB_HPROT(module, channel, protect) (dmacHw_REG_CFG_HI(module, channel) = (dmacHw_REG_CFG_HI((module), (channel)) & ~(dmacHw_REG_CFG_HI_AHB_HPROT_MASK)) | (protect))
+
+#define dmacHw_SET_CONFIG_HI(module, channel, cfg) (dmacHw_REG_CFG_HI((module), (channel)) |= (cfg))
+#define dmacHw_RESET_CONFIG_HI(module, channel) (dmacHw_REG_CFG_HI((module), (channel)) = 0)
+#define dmacHw_GET_CONFIG_HI(module, channel) (dmacHw_REG_CFG_HI((module), (channel)))
+#define dmacHw_SET_SRC_PERI_INTF(module, channel, intf) (dmacHw_REG_CFG_HI((module), (channel)) = (dmacHw_REG_CFG_HI((module), (channel)) & ~(dmacHw_REG_CFG_HI_SRC_PERI_INTF_MASK)) | (((intf) << 7) & dmacHw_REG_CFG_HI_SRC_PERI_INTF_MASK))
+#define dmacHw_SRC_PERI_INTF(intf) (((intf) << 7) & dmacHw_REG_CFG_HI_SRC_PERI_INTF_MASK)
+#define dmacHw_SET_DST_PERI_INTF(module, channel, intf) (dmacHw_REG_CFG_HI((module), (channel)) = (dmacHw_REG_CFG_HI((module), (channel)) & ~(dmacHw_REG_CFG_HI_DST_PERI_INTF_MASK)) | (((intf) << 11) & dmacHw_REG_CFG_HI_DST_PERI_INTF_MASK))
+#define dmacHw_DST_PERI_INTF(intf) (((intf) << 11) & dmacHw_REG_CFG_HI_DST_PERI_INTF_MASK)
+
+#define dmacHw_DMA_START(module, channel) (dmacHw_REG_MISC_CH_ENABLE((module)) = (0x00000001 << ((channel) + 8)) | (0x00000001 << (channel)))
+#define dmacHw_DMA_STOP(module, channel) (dmacHw_REG_MISC_CH_ENABLE((module)) = (0x00000001 << ((channel) + 8)))
+#define dmacHw_DMA_ENABLE(module) (dmacHw_REG_MISC_CFG((module)) = 1)
+#define dmacHw_DMA_DISABLE(module) (dmacHw_REG_MISC_CFG((module)) = 0)
+
+#define dmacHw_TRAN_INT_ENABLE(module, channel) (dmacHw_REG_INT_MASK_TRAN((module)) = (0x00000001 << ((channel) + 8)) | (0x00000001 << (channel)))
+#define dmacHw_BLOCK_INT_ENABLE(module, channel) (dmacHw_REG_INT_MASK_BLOCK((module)) = (0x00000001 << ((channel) + 8)) | (0x00000001 << (channel)))
+#define dmacHw_ERROR_INT_ENABLE(module, channel) (dmacHw_REG_INT_MASK_ERROR((module)) = (0x00000001 << ((channel) + 8)) | (0x00000001 << (channel)))
+
+#define dmacHw_TRAN_INT_DISABLE(module, channel) (dmacHw_REG_INT_MASK_TRAN((module)) = (0x00000001 << ((channel) + 8)))
+#define dmacHw_BLOCK_INT_DISABLE(module, channel) (dmacHw_REG_INT_MASK_BLOCK((module)) = (0x00000001 << ((channel) + 8)))
+#define dmacHw_ERROR_INT_DISABLE(module, channel) (dmacHw_REG_INT_MASK_ERROR((module)) = (0x00000001 << ((channel) + 8)))
+#define dmacHw_STRAN_INT_DISABLE(module, channel) (dmacHw_REG_INT_MASK_STRAN((module)) = (0x00000001 << ((channel) + 8)))
+#define dmacHw_DTRAN_INT_DISABLE(module, channel) (dmacHw_REG_INT_MASK_DTRAN((module)) = (0x00000001 << ((channel) + 8)))
+
+#define dmacHw_TRAN_INT_CLEAR(module, channel) (dmacHw_REG_INT_CLEAR_TRAN((module)) = (0x00000001 << (channel)))
+#define dmacHw_BLOCK_INT_CLEAR(module, channel) (dmacHw_REG_INT_CLEAR_BLOCK((module)) = (0x00000001 << (channel)))
+#define dmacHw_ERROR_INT_CLEAR(module, channel) (dmacHw_REG_INT_CLEAR_ERROR((module)) = (0x00000001 << (channel)))
+
+#define dmacHw_GET_NUM_CHANNEL(module) (((dmacHw_REG_MISC_COMP_PARAM1_HI((module)) & dmacHw_REG_COMP_PARAM_NUM_CHANNELS) >> 8) + 1)
+#define dmacHw_GET_NUM_INTERFACE(module) (((dmacHw_REG_MISC_COMP_PARAM1_HI((module)) & dmacHw_REG_COMP_PARAM_NUM_INTERFACE) >> 11) + 1)
+#define dmacHw_GET_MAX_BLOCK_SIZE(module, channel) ((dmacHw_REG_MISC_COMP_PARAM1_LO((module)) >> (4 * (channel))) & dmacHw_REG_COMP_PARAM_MAX_BLK_SIZE)
+#define dmacHw_GET_CHANNEL_DATA_WIDTH(module, channel) ((dmacHw_REG_MISC_COMP_PARAM1_HI((module)) & dmacHw_REG_COMP_PARAM_DATA_WIDTH) >> 13)
+
+#endif /* _DMACHW_REG_H */
diff --git a/arch/arm/mach-bcmring/include/mach/csp/hw_cfg.h b/arch/arm/mach-bcmring/include/mach/csp/hw_cfg.h
new file mode 100644
index 00000000000..cfa91bed9d3
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/hw_cfg.h
@@ -0,0 +1,73 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+
+#ifndef CSP_HW_CFG_H
+#define CSP_HW_CFG_H
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <cfg_global.h>
+#include <mach/csp/cap_inline.h>
+
+#if defined(__KERNEL__)
+#include <mach/memory_settings.h>
+#else
+#include <hw_cfg.h>
+#endif
+
+/* Some items that can be defined externally, but will be set to default values */
+/* if they are not defined. */
+/* HW_CFG_PLL_SPREAD_SPECTRUM_DISABLE Default undefined and SS is enabled. */
+/* HW_CFG_SDRAM_CAS_LATENCY 5 Default 5, Values [3..6] */
+/* HW_CFG_SDRAM_CHIP_SELECT_CNT 1 Default 1, Vaules [1..2] */
+/* HW_CFG_SDRAM_SPEED_GRADE 667 Default 667, Values [400,533,667,800] */
+/* HW_CFG_SDRAM_WIDTH_BITS 16 Default 16, Vaules [8,16] */
+/* HW_CFG_SDRAM_ADDR_BRC Default undefined and Row-Bank-Col (RBC) addressing used. Define to use Bank-Row-Col (BRC). */
+/* HW_CFG_SDRAM_CLK_ASYNC Default undefined and DDR clock is synchronous with AXI BUS clock. Define for ASYNC mode. */
+
+#if defined(CFG_GLOBAL_CHIP)
+ #if (CFG_GLOBAL_CHIP == FPGA11107)
+ #define HW_CFG_BUS_CLK_HZ 5000000
+ #define HW_CFG_DDR_CTLR_CLK_HZ 10000000
+ #define HW_CFG_DDR_PHY_OMIT
+ #define HW_CFG_UART_CLK_HZ 7500000
+ #else
+ #define HW_CFG_PLL_VCO_HZ 2000000000
+ #define HW_CFG_PLL2_VCO_HZ 1800000000
+ #define HW_CFG_ARM_CLK_HZ CAP_HW_CFG_ARM_CLK_HZ
+ #define HW_CFG_BUS_CLK_HZ 166666666
+ #define HW_CFG_DDR_CTLR_CLK_HZ 333333333
+ #define HW_CFG_DDR_PHY_CLK_HZ (2 * HW_CFG_DDR_CTLR_CLK_HZ)
+ #define HW_CFG_UART_CLK_HZ 142857142
+ #define HW_CFG_VPM_CLK_HZ CAP_HW_CFG_VPM_CLK_HZ
+ #endif
+#else
+ #define HW_CFG_PLL_VCO_HZ 1800000000
+ #define HW_CFG_PLL2_VCO_HZ 1800000000
+ #define HW_CFG_ARM_CLK_HZ 450000000
+ #define HW_CFG_BUS_CLK_HZ 150000000
+ #define HW_CFG_DDR_CTLR_CLK_HZ 300000000
+ #define HW_CFG_DDR_PHY_CLK_HZ (2 * HW_CFG_DDR_CTLR_CLK_HZ)
+ #define HW_CFG_UART_CLK_HZ 150000000
+ #define HW_CFG_VPM_CLK_HZ 300000000
+#endif
+
+/* ---- Public Constants and Types --------------------------------------- */
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+
+
+#endif /* CSP_HW_CFG_H */
+
diff --git a/arch/arm/mach-bcmring/include/mach/csp/intcHw_reg.h b/arch/arm/mach-bcmring/include/mach/csp/intcHw_reg.h
new file mode 100644
index 00000000000..e01fc4607c9
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/intcHw_reg.h
@@ -0,0 +1,246 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file intcHw_reg.h
+*
+* @brief platform specific interrupt controller bit assignments
+*
+* @note
+* None
+*/
+/****************************************************************************/
+
+#ifndef _INTCHW_REG_H
+#define _INTCHW_REG_H
+
+/* ---- Include Files ---------------------------------------------------- */
+#include <csp/stdint.h>
+#include <csp/reg.h>
+#include <mach/csp/mm_io.h>
+
+/* ---- Public Constants and Types --------------------------------------- */
+
+#define INTCHW_NUM_IRQ_PER_INTC 32 /* Maximum number of interrupt controllers */
+#define INTCHW_NUM_INTC 3
+
+/* Defines for interrupt controllers. This simplifies and cleans up the function calls. */
+#define INTCHW_INTC0 ((void *)MM_IO_BASE_INTC0)
+#define INTCHW_INTC1 ((void *)MM_IO_BASE_INTC1)
+#define INTCHW_SINTC ((void *)MM_IO_BASE_SINTC)
+
+/* INTC0 - interrupt controller 0 */
+#define INTCHW_INTC0_PIF_BITNUM 31 /* Peripheral interface interrupt */
+#define INTCHW_INTC0_CLCD_BITNUM 30 /* LCD Controller interrupt */
+#define INTCHW_INTC0_GE_BITNUM 29 /* Graphic engine interrupt */
+#define INTCHW_INTC0_APM_BITNUM 28 /* Audio process module interrupt */
+#define INTCHW_INTC0_ESW_BITNUM 27 /* Ethernet switch interrupt */
+#define INTCHW_INTC0_SPIH_BITNUM 26 /* SPI host interrupt */
+#define INTCHW_INTC0_TIMER3_BITNUM 25 /* Timer3 interrupt */
+#define INTCHW_INTC0_TIMER2_BITNUM 24 /* Timer2 interrupt */
+#define INTCHW_INTC0_TIMER1_BITNUM 23 /* Timer1 interrupt */
+#define INTCHW_INTC0_TIMER0_BITNUM 22 /* Timer0 interrupt */
+#define INTCHW_INTC0_SDIOH1_BITNUM 21 /* SDIO1 host interrupt */
+#define INTCHW_INTC0_SDIOH0_BITNUM 20 /* SDIO0 host interrupt */
+#define INTCHW_INTC0_USBD_BITNUM 19 /* USB device interrupt */
+#define INTCHW_INTC0_USBH1_BITNUM 18 /* USB1 host interrupt */
+#define INTCHW_INTC0_USBHD2_BITNUM 17 /* USB host2/device2 interrupt */
+#define INTCHW_INTC0_VPM_BITNUM 16 /* Voice process module interrupt */
+#define INTCHW_INTC0_DMA1C7_BITNUM 15 /* DMA1 channel 7 interrupt */
+#define INTCHW_INTC0_DMA1C6_BITNUM 14 /* DMA1 channel 6 interrupt */
+#define INTCHW_INTC0_DMA1C5_BITNUM 13 /* DMA1 channel 5 interrupt */
+#define INTCHW_INTC0_DMA1C4_BITNUM 12 /* DMA1 channel 4 interrupt */
+#define INTCHW_INTC0_DMA1C3_BITNUM 11 /* DMA1 channel 3 interrupt */
+#define INTCHW_INTC0_DMA1C2_BITNUM 10 /* DMA1 channel 2 interrupt */
+#define INTCHW_INTC0_DMA1C1_BITNUM 9 /* DMA1 channel 1 interrupt */
+#define INTCHW_INTC0_DMA1C0_BITNUM 8 /* DMA1 channel 0 interrupt */
+#define INTCHW_INTC0_DMA0C7_BITNUM 7 /* DMA0 channel 7 interrupt */
+#define INTCHW_INTC0_DMA0C6_BITNUM 6 /* DMA0 channel 6 interrupt */
+#define INTCHW_INTC0_DMA0C5_BITNUM 5 /* DMA0 channel 5 interrupt */
+#define INTCHW_INTC0_DMA0C4_BITNUM 4 /* DMA0 channel 4 interrupt */
+#define INTCHW_INTC0_DMA0C3_BITNUM 3 /* DMA0 channel 3 interrupt */
+#define INTCHW_INTC0_DMA0C2_BITNUM 2 /* DMA0 channel 2 interrupt */
+#define INTCHW_INTC0_DMA0C1_BITNUM 1 /* DMA0 channel 1 interrupt */
+#define INTCHW_INTC0_DMA0C0_BITNUM 0 /* DMA0 channel 0 interrupt */
+
+#define INTCHW_INTC0_PIF (1<<INTCHW_INTC0_PIF_BITNUM)
+#define INTCHW_INTC0_CLCD (1<<INTCHW_INTC0_CLCD_BITNUM)
+#define INTCHW_INTC0_GE (1<<INTCHW_INTC0_GE_BITNUM)
+#define INTCHW_INTC0_APM (1<<INTCHW_INTC0_APM_BITNUM)
+#define INTCHW_INTC0_ESW (1<<INTCHW_INTC0_ESW_BITNUM)
+#define INTCHW_INTC0_SPIH (1<<INTCHW_INTC0_SPIH_BITNUM)
+#define INTCHW_INTC0_TIMER3 (1<<INTCHW_INTC0_TIMER3_BITNUM)
+#define INTCHW_INTC0_TIMER2 (1<<INTCHW_INTC0_TIMER2_BITNUM)
+#define INTCHW_INTC0_TIMER1 (1<<INTCHW_INTC0_TIMER1_BITNUM)
+#define INTCHW_INTC0_TIMER0 (1<<INTCHW_INTC0_TIMER0_BITNUM)
+#define INTCHW_INTC0_SDIOH1 (1<<INTCHW_INTC0_SDIOH1_BITNUM)
+#define INTCHW_INTC0_SDIOH0 (1<<INTCHW_INTC0_SDIOH0_BITNUM)
+#define INTCHW_INTC0_USBD (1<<INTCHW_INTC0_USBD_BITNUM)
+#define INTCHW_INTC0_USBH1 (1<<INTCHW_INTC0_USBH1_BITNUM)
+#define INTCHW_INTC0_USBHD2 (1<<INTCHW_INTC0_USBHD2_BITNUM)
+#define INTCHW_INTC0_VPM (1<<INTCHW_INTC0_VPM_BITNUM)
+#define INTCHW_INTC0_DMA1C7 (1<<INTCHW_INTC0_DMA1C7_BITNUM)
+#define INTCHW_INTC0_DMA1C6 (1<<INTCHW_INTC0_DMA1C6_BITNUM)
+#define INTCHW_INTC0_DMA1C5 (1<<INTCHW_INTC0_DMA1C5_BITNUM)
+#define INTCHW_INTC0_DMA1C4 (1<<INTCHW_INTC0_DMA1C4_BITNUM)
+#define INTCHW_INTC0_DMA1C3 (1<<INTCHW_INTC0_DMA1C3_BITNUM)
+#define INTCHW_INTC0_DMA1C2 (1<<INTCHW_INTC0_DMA1C2_BITNUM)
+#define INTCHW_INTC0_DMA1C1 (1<<INTCHW_INTC0_DMA1C1_BITNUM)
+#define INTCHW_INTC0_DMA1C0 (1<<INTCHW_INTC0_DMA1C0_BITNUM)
+#define INTCHW_INTC0_DMA0C7 (1<<INTCHW_INTC0_DMA0C7_BITNUM)
+#define INTCHW_INTC0_DMA0C6 (1<<INTCHW_INTC0_DMA0C6_BITNUM)
+#define INTCHW_INTC0_DMA0C5 (1<<INTCHW_INTC0_DMA0C5_BITNUM)
+#define INTCHW_INTC0_DMA0C4 (1<<INTCHW_INTC0_DMA0C4_BITNUM)
+#define INTCHW_INTC0_DMA0C3 (1<<INTCHW_INTC0_DMA0C3_BITNUM)
+#define INTCHW_INTC0_DMA0C2 (1<<INTCHW_INTC0_DMA0C2_BITNUM)
+#define INTCHW_INTC0_DMA0C1 (1<<INTCHW_INTC0_DMA0C1_BITNUM)
+#define INTCHW_INTC0_DMA0C0 (1<<INTCHW_INTC0_DMA0C0_BITNUM)
+
+/* INTC1 - interrupt controller 1 */
+#define INTCHW_INTC1_DDRVPMP_BITNUM 27 /* DDR and VPM PLL clock phase relationship interupt (Not for A0) */
+#define INTCHW_INTC1_DDRVPMT_BITNUM 26 /* DDR and VPM HW phase align timeout interrupt (Not for A0) */
+#define INTCHW_INTC1_DDRP_BITNUM 26 /* DDR and PLL clock phase relationship interupt (For A0 only)) */
+#define INTCHW_INTC1_RTC2_BITNUM 25 /* Real time clock tamper interrupt */
+#define INTCHW_INTC1_VDEC_BITNUM 24 /* Hantro Video Decoder interrupt */
+/* Bits 13-23 are non-secure versions of the corresponding secure bits in SINTC bits 0-10. */
+#define INTCHW_INTC1_SPUM_BITNUM 23 /* Secure process module interrupt */
+#define INTCHW_INTC1_RTC1_BITNUM 22 /* Real time clock one-shot interrupt */
+#define INTCHW_INTC1_RTC0_BITNUM 21 /* Real time clock periodic interrupt */
+#define INTCHW_INTC1_RNG_BITNUM 20 /* Random number generator interrupt */
+#define INTCHW_INTC1_FMPU_BITNUM 19 /* Flash memory parition unit interrupt */
+#define INTCHW_INTC1_VMPU_BITNUM 18 /* VRAM memory partition interrupt */
+#define INTCHW_INTC1_DMPU_BITNUM 17 /* DDR2 memory partition interrupt */
+#define INTCHW_INTC1_KEYC_BITNUM 16 /* Key pad controller interrupt */
+#define INTCHW_INTC1_TSC_BITNUM 15 /* Touch screen controller interrupt */
+#define INTCHW_INTC1_UART0_BITNUM 14 /* UART 0 */
+#define INTCHW_INTC1_WDOG_BITNUM 13 /* Watchdog timer interrupt */
+
+#define INTCHW_INTC1_UART1_BITNUM 12 /* UART 1 */
+#define INTCHW_INTC1_PMUIRQ_BITNUM 11 /* ARM performance monitor interrupt */
+#define INTCHW_INTC1_COMMRX_BITNUM 10 /* ARM DDC receive interrupt */
+#define INTCHW_INTC1_COMMTX_BITNUM 9 /* ARM DDC transmit interrupt */
+#define INTCHW_INTC1_FLASHC_BITNUM 8 /* Flash controller interrupt */
+#define INTCHW_INTC1_GPHY_BITNUM 7 /* Gigabit Phy interrupt */
+#define INTCHW_INTC1_SPIS_BITNUM 6 /* SPI slave interrupt */
+#define INTCHW_INTC1_I2CS_BITNUM 5 /* I2C slave interrupt */
+#define INTCHW_INTC1_I2CH_BITNUM 4 /* I2C host interrupt */
+#define INTCHW_INTC1_I2S1_BITNUM 3 /* I2S1 interrupt */
+#define INTCHW_INTC1_I2S0_BITNUM 2 /* I2S0 interrupt */
+#define INTCHW_INTC1_GPIO1_BITNUM 1 /* GPIO bit 64//32 combined interrupt */
+#define INTCHW_INTC1_GPIO0_BITNUM 0 /* GPIO bit 31//0 combined interrupt */
+
+#define INTCHW_INTC1_DDRVPMT (1<<INTCHW_INTC1_DDRVPMT_BITNUM)
+#define INTCHW_INTC1_DDRVPMP (1<<INTCHW_INTC1_DDRVPMP_BITNUM)
+#define INTCHW_INTC1_DDRP (1<<INTCHW_INTC1_DDRP_BITNUM)
+#define INTCHW_INTC1_VDEC (1<<INTCHW_INTC1_VDEC_BITNUM)
+#define INTCHW_INTC1_SPUM (1<<INTCHW_INTC1_SPUM_BITNUM)
+#define INTCHW_INTC1_RTC2 (1<<INTCHW_INTC1_RTC2_BITNUM)
+#define INTCHW_INTC1_RTC1 (1<<INTCHW_INTC1_RTC1_BITNUM)
+#define INTCHW_INTC1_RTC0 (1<<INTCHW_INTC1_RTC0_BITNUM)
+#define INTCHW_INTC1_RNG (1<<INTCHW_INTC1_RNG_BITNUM)
+#define INTCHW_INTC1_FMPU (1<<INTCHW_INTC1_FMPU_BITNUM)
+#define INTCHW_INTC1_IMPU (1<<INTCHW_INTC1_IMPU_BITNUM)
+#define INTCHW_INTC1_DMPU (1<<INTCHW_INTC1_DMPU_BITNUM)
+#define INTCHW_INTC1_KEYC (1<<INTCHW_INTC1_KEYC_BITNUM)
+#define INTCHW_INTC1_TSC (1<<INTCHW_INTC1_TSC_BITNUM)
+#define INTCHW_INTC1_UART0 (1<<INTCHW_INTC1_UART0_BITNUM)
+#define INTCHW_INTC1_WDOG (1<<INTCHW_INTC1_WDOG_BITNUM)
+#define INTCHW_INTC1_UART1 (1<<INTCHW_INTC1_UART1_BITNUM)
+#define INTCHW_INTC1_PMUIRQ (1<<INTCHW_INTC1_PMUIRQ_BITNUM)
+#define INTCHW_INTC1_COMMRX (1<<INTCHW_INTC1_COMMRX_BITNUM)
+#define INTCHW_INTC1_COMMTX (1<<INTCHW_INTC1_COMMTX_BITNUM)
+#define INTCHW_INTC1_FLASHC (1<<INTCHW_INTC1_FLASHC_BITNUM)
+#define INTCHW_INTC1_GPHY (1<<INTCHW_INTC1_GPHY_BITNUM)
+#define INTCHW_INTC1_SPIS (1<<INTCHW_INTC1_SPIS_BITNUM)
+#define INTCHW_INTC1_I2CS (1<<INTCHW_INTC1_I2CS_BITNUM)
+#define INTCHW_INTC1_I2CH (1<<INTCHW_INTC1_I2CH_BITNUM)
+#define INTCHW_INTC1_I2S1 (1<<INTCHW_INTC1_I2S1_BITNUM)
+#define INTCHW_INTC1_I2S0 (1<<INTCHW_INTC1_I2S0_BITNUM)
+#define INTCHW_INTC1_GPIO1 (1<<INTCHW_INTC1_GPIO1_BITNUM)
+#define INTCHW_INTC1_GPIO0 (1<<INTCHW_INTC1_GPIO0_BITNUM)
+
+/* SINTC secure int controller */
+#define INTCHW_SINTC_RTC2_BITNUM 15 /* Real time clock tamper interrupt */
+#define INTCHW_SINTC_TIMER3_BITNUM 14 /* Secure timer3 interrupt */
+#define INTCHW_SINTC_TIMER2_BITNUM 13 /* Secure timer2 interrupt */
+#define INTCHW_SINTC_TIMER1_BITNUM 12 /* Secure timer1 interrupt */
+#define INTCHW_SINTC_TIMER0_BITNUM 11 /* Secure timer0 interrupt */
+#define INTCHW_SINTC_SPUM_BITNUM 10 /* Secure process module interrupt */
+#define INTCHW_SINTC_RTC1_BITNUM 9 /* Real time clock one-shot interrupt */
+#define INTCHW_SINTC_RTC0_BITNUM 8 /* Real time clock periodic interrupt */
+#define INTCHW_SINTC_RNG_BITNUM 7 /* Random number generator interrupt */
+#define INTCHW_SINTC_FMPU_BITNUM 6 /* Flash memory parition unit interrupt */
+#define INTCHW_SINTC_VMPU_BITNUM 5 /* VRAM memory partition interrupt */
+#define INTCHW_SINTC_DMPU_BITNUM 4 /* DDR2 memory partition interrupt */
+#define INTCHW_SINTC_KEYC_BITNUM 3 /* Key pad controller interrupt */
+#define INTCHW_SINTC_TSC_BITNUM 2 /* Touch screen controller interrupt */
+#define INTCHW_SINTC_UART0_BITNUM 1 /* UART0 interrupt */
+#define INTCHW_SINTC_WDOG_BITNUM 0 /* Watchdog timer interrupt */
+
+#define INTCHW_SINTC_TIMER3 (1<<INTCHW_SINTC_TIMER3_BITNUM)
+#define INTCHW_SINTC_TIMER2 (1<<INTCHW_SINTC_TIMER2_BITNUM)
+#define INTCHW_SINTC_TIMER1 (1<<INTCHW_SINTC_TIMER1_BITNUM)
+#define INTCHW_SINTC_TIMER0 (1<<INTCHW_SINTC_TIMER0_BITNUM)
+#define INTCHW_SINTC_SPUM (1<<INTCHW_SINTC_SPUM_BITNUM)
+#define INTCHW_SINTC_RTC2 (1<<INTCHW_SINTC_RTC2_BITNUM)
+#define INTCHW_SINTC_RTC1 (1<<INTCHW_SINTC_RTC1_BITNUM)
+#define INTCHW_SINTC_RTC0 (1<<INTCHW_SINTC_RTC0_BITNUM)
+#define INTCHW_SINTC_RNG (1<<INTCHW_SINTC_RNG_BITNUM)
+#define INTCHW_SINTC_FMPU (1<<INTCHW_SINTC_FMPU_BITNUM)
+#define INTCHW_SINTC_IMPU (1<<INTCHW_SINTC_IMPU_BITNUM)
+#define INTCHW_SINTC_DMPU (1<<INTCHW_SINTC_DMPU_BITNUM)
+#define INTCHW_SINTC_KEYC (1<<INTCHW_SINTC_KEYC_BITNUM)
+#define INTCHW_SINTC_TSC (1<<INTCHW_SINTC_TSC_BITNUM)
+#define INTCHW_SINTC_UART0 (1<<INTCHW_SINTC_UART0_BITNUM)
+#define INTCHW_SINTC_WDOG (1<<INTCHW_SINTC_WDOG_BITNUM)
+
+/* PL192 Vectored Interrupt Controller (VIC) layout */
+#define INTCHW_IRQSTATUS 0x00 /* IRQ status register */
+#define INTCHW_FIQSTATUS 0x04 /* FIQ status register */
+#define INTCHW_RAWINTR 0x08 /* Raw Interrupt Status register */
+#define INTCHW_INTSELECT 0x0c /* Interrupt Select Register */
+#define INTCHW_INTENABLE 0x10 /* Interrupt Enable Register */
+#define INTCHW_INTENCLEAR 0x14 /* Interrupt Enable Clear Register */
+#define INTCHW_SOFTINT 0x18 /* Soft Interrupt Register */
+#define INTCHW_SOFTINTCLEAR 0x1c /* Soft Interrupt Clear Register */
+#define INTCHW_PROTECTION 0x20 /* Protection Enable Register */
+#define INTCHW_SWPRIOMASK 0x24 /* Software Priority Mask Register */
+#define INTCHW_PRIODAISY 0x28 /* Priority Daisy Chain Register */
+#define INTCHW_VECTADDR0 0x100 /* Vector Address Registers */
+#define INTCHW_VECTPRIO0 0x200 /* Vector Priority Registers 0-31 */
+#define INTCHW_ADDRESS 0xf00 /* Vector Address Register 0-31 */
+#define INTCHW_PID 0xfe0 /* Peripheral ID Register 0-3 */
+#define INTCHW_PCELLID 0xff0 /* PrimeCell ID Register 0-3 */
+
+/* Example Usage: intcHw_irq_enable(INTCHW_INTC0, INTCHW_INTC0_TIMER0); */
+/* intcHw_irq_clear(INTCHW_INTC0, INTCHW_INTC0_TIMER0); */
+/* uint32_t bits = intcHw_irq_status(INTCHW_INTC0); */
+/* uint32_t bits = intcHw_irq_raw_status(INTCHW_INTC0); */
+
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+/* Clear one or more IRQ interrupts. */
+static inline void intcHw_irq_disable(void *basep, uint32_t mask)
+{
+ __REG32(basep + INTCHW_INTENCLEAR) = mask;
+}
+
+/* Enables one or more IRQ interrupts. */
+static inline void intcHw_irq_enable(void *basep, uint32_t mask)
+{
+ __REG32(basep + INTCHW_INTENABLE) = mask;
+}
+
+#endif /* _INTCHW_REG_H */
diff --git a/arch/arm/mach-bcmring/include/mach/csp/mm_addr.h b/arch/arm/mach-bcmring/include/mach/csp/mm_addr.h
new file mode 100644
index 00000000000..86bb58d4f58
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/mm_addr.h
@@ -0,0 +1,101 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file mm_addr.h
+*
+* @brief Memory Map address defintions
+*
+* @note
+* None
+*/
+/****************************************************************************/
+
+#ifndef _MM_ADDR_H
+#define _MM_ADDR_H
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#if !defined(CSP_SIMULATION)
+#include <cfg_global.h>
+#endif
+
+/* ---- Public Constants and Types --------------------------------------- */
+
+/* Memory Map address definitions */
+
+#define MM_ADDR_DDR 0x00000000
+
+#define MM_ADDR_IO_VPM_EXTMEM_RSVD 0x0F000000 /* 16 MB - Reserved external memory for VPM use */
+
+#define MM_ADDR_IO_FLASHC 0x20000000
+#define MM_ADDR_IO_BROM 0x30000000
+#define MM_ADDR_IO_ARAM 0x30100000 /* 64 KB - extra cycle latency - WS switch */
+#define MM_ADDR_IO_DMA0 0x30200000
+#define MM_ADDR_IO_DMA1 0x30300000
+#define MM_ADDR_IO_ESW 0x30400000
+#define MM_ADDR_IO_CLCD 0x30500000
+#define MM_ADDR_IO_PIF 0x30580000
+#define MM_ADDR_IO_APM 0x30600000
+#define MM_ADDR_IO_SPUM 0x30700000
+#define MM_ADDR_IO_VPM_PROG 0x30800000
+#define MM_ADDR_IO_VPM_DATA 0x30A00000
+#define MM_ADDR_IO_VRAM 0x40000000 /* 64 KB - security block in front of it */
+#define MM_ADDR_IO_CHIPC 0x80000000
+#define MM_ADDR_IO_UMI 0x80001000
+#define MM_ADDR_IO_NAND 0x80001800
+#define MM_ADDR_IO_LEDM 0x80002000
+#define MM_ADDR_IO_PWM 0x80002040
+#define MM_ADDR_IO_VINTC 0x80003000
+#define MM_ADDR_IO_GPIO0 0x80004000
+#define MM_ADDR_IO_GPIO1 0x80004800
+#define MM_ADDR_IO_I2CS 0x80005000
+#define MM_ADDR_IO_SPIS 0x80006000
+#define MM_ADDR_IO_HPM 0x80007400
+#define MM_ADDR_IO_HPM_REMAP 0x80007800
+#define MM_ADDR_IO_TZPC 0x80008000
+#define MM_ADDR_IO_MPU 0x80009000
+#define MM_ADDR_IO_SPUMP 0x8000a000
+#define MM_ADDR_IO_PKA 0x8000b000
+#define MM_ADDR_IO_RNG 0x8000c000
+#define MM_ADDR_IO_KEYC 0x8000d000
+#define MM_ADDR_IO_BBL 0x8000e000
+#define MM_ADDR_IO_OTP 0x8000f000
+#define MM_ADDR_IO_I2S0 0x80010000
+#define MM_ADDR_IO_I2S1 0x80011000
+#define MM_ADDR_IO_UARTA 0x80012000
+#define MM_ADDR_IO_UARTB 0x80013000
+#define MM_ADDR_IO_I2CH 0x80014020
+#define MM_ADDR_IO_SPIH 0x80015000
+#define MM_ADDR_IO_TSC 0x80016000
+#define MM_ADDR_IO_TMR 0x80017000
+#define MM_ADDR_IO_WATCHDOG 0x80017800
+#define MM_ADDR_IO_ETM 0x80018000
+#define MM_ADDR_IO_DDRC 0x80019000
+#define MM_ADDR_IO_SINTC 0x80100000
+#define MM_ADDR_IO_INTC0 0x80200000
+#define MM_ADDR_IO_INTC1 0x80201000
+#define MM_ADDR_IO_GE 0x80300000
+#define MM_ADDR_IO_USB_CTLR0 0x80400000
+#define MM_ADDR_IO_USB_CTLR1 0x80410000
+#define MM_ADDR_IO_USB_PHY 0x80420000
+#define MM_ADDR_IO_SDIOH0 0x80500000
+#define MM_ADDR_IO_SDIOH1 0x80600000
+#define MM_ADDR_IO_VDEC 0x80700000
+
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+
+#endif /* _MM_ADDR_H */
diff --git a/arch/arm/mach-bcmring/include/mach/csp/mm_io.h b/arch/arm/mach-bcmring/include/mach/csp/mm_io.h
new file mode 100644
index 00000000000..de92ec6a01a
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/mm_io.h
@@ -0,0 +1,147 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file mm_io.h
+*
+* @brief Memory Map I/O definitions
+*
+* @note
+* None
+*/
+/****************************************************************************/
+
+#ifndef _MM_IO_H
+#define _MM_IO_H
+
+/* ---- Include Files ---------------------------------------------------- */
+#include <mach/csp/mm_addr.h>
+
+#if !defined(CSP_SIMULATION)
+#include <cfg_global.h>
+#endif
+
+/* ---- Public Constants and Types --------------------------------------- */
+
+#if defined(CONFIG_MMU)
+
+/* This macro is referenced in <mach/io.h>
+ * Phys to Virtual 0xNyxxxxxx => 0xFNxxxxxx
+ * This macro is referenced in <asm/arch/io.h>
+ *
+ * Assume VPM address is the last x MB of memory. For VPM, map to
+ * 0xf0000000 and up.
+ */
+
+#ifndef MM_IO_PHYS_TO_VIRT
+#ifdef __ASSEMBLY__
+#define MM_IO_PHYS_TO_VIRT(phys) (0xF0000000 | (((phys) >> 4) & 0x0F000000) | ((phys) & 0xFFFFFF))
+#else
+#define MM_IO_PHYS_TO_VIRT(phys) (((phys) == MM_ADDR_IO_VPM_EXTMEM_RSVD) ? 0xF0000000 : \
+ (0xF0000000 | (((phys) >> 4) & 0x0F000000) | ((phys) & 0xFFFFFF)))
+#endif
+#endif
+
+/* Virtual to Physical 0xFNxxxxxx => 0xN0xxxxxx */
+
+#ifndef MM_IO_VIRT_TO_PHYS
+#ifdef __ASSEMBLY__
+#define MM_IO_VIRT_TO_PHYS(virt) ((((virt) & 0x0F000000) << 4) | ((virt) & 0xFFFFFF))
+#else
+#define MM_IO_VIRT_TO_PHYS(virt) (((virt) == 0xF0000000) ? MM_ADDR_IO_VPM_EXTMEM_RSVD : \
+ ((((virt) & 0x0F000000) << 4) | ((virt) & 0xFFFFFF)))
+#endif
+#endif
+
+#else
+
+#ifndef MM_IO_PHYS_TO_VIRT
+#define MM_IO_PHYS_TO_VIRT(phys) (phys)
+#endif
+
+#ifndef MM_IO_VIRT_TO_PHYS
+#define MM_IO_VIRT_TO_PHYS(virt) (virt)
+#endif
+
+#endif
+
+/* Registers in 0xExxxxxxx that should be moved to 0xFxxxxxxx */
+#define MM_IO_BASE_FLASHC MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_FLASHC)
+#define MM_IO_BASE_NAND MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_NAND)
+#define MM_IO_BASE_UMI MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_UMI)
+
+#define MM_IO_START MM_ADDR_IO_FLASHC /* Physical beginning of IO mapped memory */
+#define MM_IO_BASE MM_IO_BASE_FLASHC /* Virtual beginning of IO mapped memory */
+
+#define MM_IO_BASE_BROM MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_BROM)
+#define MM_IO_BASE_ARAM MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_ARAM)
+#define MM_IO_BASE_DMA0 MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_DMA0)
+#define MM_IO_BASE_DMA1 MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_DMA1)
+#define MM_IO_BASE_ESW MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_ESW)
+#define MM_IO_BASE_CLCD MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_CLCD)
+#define MM_IO_BASE_PIF MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_PIF)
+#define MM_IO_BASE_APM MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_APM)
+#define MM_IO_BASE_SPUM MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_SPUM)
+#define MM_IO_BASE_VPM_PROG MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_VPM_PROG)
+#define MM_IO_BASE_VPM_DATA MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_VPM_DATA)
+
+#define MM_IO_BASE_VRAM MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_VRAM)
+
+#define MM_IO_BASE_CHIPC MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_CHIPC)
+#define MM_IO_BASE_DDRC MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_DDRC)
+#define MM_IO_BASE_LEDM MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_LEDM)
+#define MM_IO_BASE_PWM MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_PWM)
+#define MM_IO_BASE_VINTC MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_VINTC)
+#define MM_IO_BASE_GPIO0 MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_GPIO0)
+#define MM_IO_BASE_GPIO1 MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_GPIO1)
+#define MM_IO_BASE_TMR MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_TMR)
+#define MM_IO_BASE_WATCHDOG MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_WATCHDOG)
+#define MM_IO_BASE_ETM MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_ETM)
+#define MM_IO_BASE_HPM MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_HPM)
+#define MM_IO_BASE_HPM_REMAP MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_HPM_REMAP)
+#define MM_IO_BASE_TZPC MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_TZPC)
+#define MM_IO_BASE_MPU MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_MPU)
+#define MM_IO_BASE_SPUMP MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_SPUMP)
+#define MM_IO_BASE_PKA MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_PKA)
+#define MM_IO_BASE_RNG MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_RNG)
+#define MM_IO_BASE_KEYC MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_KEYC)
+#define MM_IO_BASE_BBL MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_BBL)
+#define MM_IO_BASE_OTP MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_OTP)
+#define MM_IO_BASE_I2S0 MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_I2S0)
+#define MM_IO_BASE_I2S1 MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_I2S1)
+#define MM_IO_BASE_UARTA MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_UARTA)
+#define MM_IO_BASE_UARTB MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_UARTB)
+#define MM_IO_BASE_I2CH MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_I2CH)
+#define MM_IO_BASE_SPIH MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_SPIH)
+#define MM_IO_BASE_TSC MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_TSC)
+#define MM_IO_BASE_I2CS MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_I2CS)
+#define MM_IO_BASE_SPIS MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_SPIS)
+#define MM_IO_BASE_SINTC MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_SINTC)
+#define MM_IO_BASE_INTC0 MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_INTC0)
+#define MM_IO_BASE_INTC1 MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_INTC1)
+#define MM_IO_BASE_GE MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_GE)
+#define MM_IO_BASE_USB_CTLR0 MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_USB_CTLR0)
+#define MM_IO_BASE_USB_CTLR1 MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_USB_CTLR1)
+#define MM_IO_BASE_USB_PHY MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_USB_PHY)
+#define MM_IO_BASE_SDIOH0 MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_SDIOH0)
+#define MM_IO_BASE_SDIOH1 MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_SDIOH1)
+#define MM_IO_BASE_VDEC MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_VDEC)
+
+#define MM_IO_BASE_VPM_EXTMEM_RSVD MM_IO_PHYS_TO_VIRT(MM_ADDR_IO_VPM_EXTMEM_RSVD)
+
+/* ---- Public Variable Externs ------------------------------------------ */
+/* ---- Public Function Prototypes --------------------------------------- */
+
+#endif /* _MM_IO_H */
diff --git a/arch/arm/mach-bcmring/include/mach/csp/secHw_def.h b/arch/arm/mach-bcmring/include/mach/csp/secHw_def.h
new file mode 100644
index 00000000000..d15f5f3ec2d
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/secHw_def.h
@@ -0,0 +1,100 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file secHw_def.h
+*
+* @brief Definitions for configuring/testing secure blocks
+*
+* @note
+* None
+*/
+/****************************************************************************/
+
+#ifndef SECHW_DEF_H
+#define SECHW_DEF_H
+
+#include <mach/csp/mm_io.h>
+
+/* Bit mask for various secure device */
+#define secHw_BLK_MASK_CHIP_CONTROL 0x00000001
+#define secHw_BLK_MASK_KEY_SCAN 0x00000002
+#define secHw_BLK_MASK_TOUCH_SCREEN 0x00000004
+#define secHw_BLK_MASK_UART0 0x00000008
+#define secHw_BLK_MASK_UART1 0x00000010
+#define secHw_BLK_MASK_WATCHDOG 0x00000020
+#define secHw_BLK_MASK_SPUM 0x00000040
+#define secHw_BLK_MASK_DDR2 0x00000080
+#define secHw_BLK_MASK_EXT_MEM 0x00000100
+#define secHw_BLK_MASK_ESW 0x00000200
+#define secHw_BLK_MASK_SPU 0x00010000
+#define secHw_BLK_MASK_PKA 0x00020000
+#define secHw_BLK_MASK_RNG 0x00040000
+#define secHw_BLK_MASK_RTC 0x00080000
+#define secHw_BLK_MASK_OTP 0x00100000
+#define secHw_BLK_MASK_BOOT 0x00200000
+#define secHw_BLK_MASK_MPU 0x00400000
+#define secHw_BLK_MASK_TZCTRL 0x00800000
+#define secHw_BLK_MASK_INTR 0x01000000
+
+/* Trustzone register set */
+typedef struct {
+ volatile uint32_t status; /* read only - reflects status of writes of 2 write registers */
+ volatile uint32_t setUnsecure; /* write only. reads back as 0 */
+ volatile uint32_t setSecure; /* write only. reads back as 0 */
+} secHw_TZREG_t;
+
+/* There are 2 register sets. The first is for the lower 16 bits, the 2nd */
+/* is for the higher 16 bits. */
+
+typedef enum {
+ secHw_IDX_LS = 0,
+ secHw_IDX_MS = 1,
+ secHw_IDX_NUM
+} secHw_IDX_e;
+
+typedef struct {
+ volatile secHw_TZREG_t reg[secHw_IDX_NUM];
+} secHw_REGS_t;
+
+/****************************************************************************/
+/**
+* @brief Configures a device as a secure device
+*
+*/
+/****************************************************************************/
+static inline void secHw_setSecure(uint32_t mask /* mask of type secHw_BLK_MASK_XXXXXX */
+ );
+
+/****************************************************************************/
+/**
+* @brief Configures a device as a non-secure device
+*
+*/
+/****************************************************************************/
+static inline void secHw_setUnsecure(uint32_t mask /* mask of type secHw_BLK_MASK_XXXXXX */
+ );
+
+/****************************************************************************/
+/**
+* @brief Get the trustzone status for all components. 1 = non-secure, 0 = secure
+*
+*/
+/****************************************************************************/
+static inline uint32_t secHw_getStatus(void);
+
+#include <mach/csp/secHw_inline.h>
+
+#endif /* SECHW_DEF_H */
diff --git a/arch/arm/mach-bcmring/include/mach/csp/secHw_inline.h b/arch/arm/mach-bcmring/include/mach/csp/secHw_inline.h
new file mode 100644
index 00000000000..9cd6a032ab7
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/secHw_inline.h
@@ -0,0 +1,79 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file secHw_inline.h
+*
+* @brief Definitions for configuring/testing secure blocks
+*
+* @note
+* None
+*/
+/****************************************************************************/
+
+#ifndef SECHW_INLINE_H
+#define SECHW_INLINE_H
+
+/****************************************************************************/
+/**
+* @brief Configures a device as a secure device
+*
+*/
+/****************************************************************************/
+static inline void secHw_setSecure(uint32_t mask /* mask of type secHw_BLK_MASK_XXXXXX */
+ ) {
+ secHw_REGS_t *regp = (secHw_REGS_t *) MM_IO_BASE_TZPC;
+
+ if (mask & 0x0000FFFF) {
+ regp->reg[secHw_IDX_LS].setSecure = mask & 0x0000FFFF;
+ }
+
+ if (mask & 0xFFFF0000) {
+ regp->reg[secHw_IDX_MS].setSecure = mask >> 16;
+ }
+}
+
+/****************************************************************************/
+/**
+* @brief Configures a device as a non-secure device
+*
+*/
+/****************************************************************************/
+static inline void secHw_setUnsecure(uint32_t mask /* mask of type secHw_BLK_MASK_XXXXXX */
+ ) {
+ secHw_REGS_t *regp = (secHw_REGS_t *) MM_IO_BASE_TZPC;
+
+ if (mask & 0x0000FFFF) {
+ regp->reg[secHw_IDX_LS].setUnsecure = mask & 0x0000FFFF;
+ }
+ if (mask & 0xFFFF0000) {
+ regp->reg[secHw_IDX_MS].setUnsecure = mask >> 16;
+ }
+}
+
+/****************************************************************************/
+/**
+* @brief Get the trustzone status for all components. 1 = non-secure, 0 = secure
+*
+*/
+/****************************************************************************/
+static inline uint32_t secHw_getStatus(void)
+{
+ secHw_REGS_t *regp = (secHw_REGS_t *) MM_IO_BASE_TZPC;
+
+ return (regp->reg[1].status << 16) + regp->reg[0].status;
+}
+
+#endif /* SECHW_INLINE_H */
diff --git a/arch/arm/mach-bcmring/include/mach/csp/tmrHw_reg.h b/arch/arm/mach-bcmring/include/mach/csp/tmrHw_reg.h
new file mode 100644
index 00000000000..3080ac7239a
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/csp/tmrHw_reg.h
@@ -0,0 +1,82 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file tmrHw_reg.h
+*
+* @brief Definitions for low level Timer registers
+*
+*/
+/****************************************************************************/
+#ifndef _TMRHW_REG_H
+#define _TMRHW_REG_H
+
+#include <mach/csp/mm_io.h>
+#include <mach/csp/hw_cfg.h>
+/* Base address */
+#define tmrHw_MODULE_BASE_ADDR MM_IO_BASE_TMR
+
+/*
+This platform has four different timers running at different clock speed
+
+Timer one (Timer ID 0) runs at 25 MHz
+Timer two (Timer ID 1) runs at 25 MHz
+Timer three (Timer ID 2) runs at 150 MHz
+Timer four (Timer ID 3) runs at 150 MHz
+*/
+#define tmrHw_LOW_FREQUENCY_MHZ 25 /* Always 25MHz from XTAL */
+#define tmrHw_LOW_FREQUENCY_HZ 25000000
+
+#if defined(CFG_GLOBAL_CHIP) && (CFG_GLOBAL_CHIP == FPGA11107)
+#define tmrHw_HIGH_FREQUENCY_MHZ 150 /* Always 150MHz for FPGA */
+#define tmrHw_HIGH_FREQUENCY_HZ 150000000
+#else
+#define tmrHw_HIGH_FREQUENCY_HZ HW_CFG_BUS_CLK_HZ
+#define tmrHw_HIGH_FREQUENCY_MHZ (HW_CFG_BUS_CLK_HZ / 1000000)
+#endif
+
+#define tmrHw_LOW_RESOLUTION_CLOCK tmrHw_LOW_FREQUENCY_HZ
+#define tmrHw_HIGH_RESOLUTION_CLOCK tmrHw_HIGH_FREQUENCY_HZ
+#define tmrHw_MAX_COUNT (0xFFFFFFFF) /* maximum number of count a timer can count */
+#define tmrHw_TIMER_NUM_COUNT (4) /* Number of timer module supported */
+
+typedef struct {
+ uint32_t LoadValue; /* Load value for timer */
+ uint32_t CurrentValue; /* Current value for timer */
+ uint32_t Control; /* Control register */
+ uint32_t InterruptClear; /* Interrupt clear register */
+ uint32_t RawInterruptStatus; /* Raw interrupt status */
+ uint32_t InterruptStatus; /* Masked interrupt status */
+ uint32_t BackgroundLoad; /* Background load value */
+ uint32_t padding; /* Padding register */
+} tmrHw_REG_t;
+
+/* Control bot masks */
+#define tmrHw_CONTROL_TIMER_ENABLE 0x00000080
+#define tmrHw_CONTROL_PERIODIC 0x00000040
+#define tmrHw_CONTROL_INTERRUPT_ENABLE 0x00000020
+#define tmrHw_CONTROL_PRESCALE_MASK 0x0000000C
+#define tmrHw_CONTROL_PRESCALE_1 0x00000000
+#define tmrHw_CONTROL_PRESCALE_16 0x00000004
+#define tmrHw_CONTROL_PRESCALE_256 0x00000008
+#define tmrHw_CONTROL_32BIT 0x00000002
+#define tmrHw_CONTROL_ONESHOT 0x00000001
+#define tmrHw_CONTROL_FREE_RUNNING 0x00000000
+
+#define tmrHw_CONTROL_MODE_MASK (tmrHw_CONTROL_PERIODIC | tmrHw_CONTROL_ONESHOT)
+
+#define pTmrHw ((volatile tmrHw_REG_t *)tmrHw_MODULE_BASE_ADDR)
+
+#endif /* _TMRHW_REG_H */
diff --git a/arch/arm/mach-bcmring/include/mach/dma.h b/arch/arm/mach-bcmring/include/mach/dma.h
new file mode 100644
index 00000000000..847980c85c8
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/dma.h
@@ -0,0 +1,826 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/****************************************************************************/
+/**
+* @file dma.h
+*
+* @brief API definitions for the linux DMA interface.
+*/
+/****************************************************************************/
+
+#if !defined(ASM_ARM_ARCH_BCMRING_DMA_H)
+#define ASM_ARM_ARCH_BCMRING_DMA_H
+
+/* ---- Include Files ---------------------------------------------------- */
+
+#include <linux/kernel.h>
+#include <linux/wait.h>
+#include <linux/semaphore.h>
+#include <csp/dmacHw.h>
+#include <mach/timer.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+
+/* ---- Constants and Types ---------------------------------------------- */
+
+/* If DMA_DEBUG_TRACK_RESERVATION is set to a non-zero value, then the filename */
+/* and line number of the reservation request will be recorded in the channel table */
+
+#define DMA_DEBUG_TRACK_RESERVATION 1
+
+#define DMA_NUM_CONTROLLERS 2
+#define DMA_NUM_CHANNELS 8 /* per controller */
+
+typedef enum {
+ DMA_DEVICE_MEM_TO_MEM, /* For memory to memory transfers */
+ DMA_DEVICE_I2S0_DEV_TO_MEM,
+ DMA_DEVICE_I2S0_MEM_TO_DEV,
+ DMA_DEVICE_I2S1_DEV_TO_MEM,
+ DMA_DEVICE_I2S1_MEM_TO_DEV,
+ DMA_DEVICE_APM_CODEC_A_DEV_TO_MEM,
+ DMA_DEVICE_APM_CODEC_A_MEM_TO_DEV,
+ DMA_DEVICE_APM_CODEC_B_DEV_TO_MEM,
+ DMA_DEVICE_APM_CODEC_B_MEM_TO_DEV,
+ DMA_DEVICE_APM_CODEC_C_DEV_TO_MEM, /* Additional mic input for beam-forming */
+ DMA_DEVICE_APM_PCM0_DEV_TO_MEM,
+ DMA_DEVICE_APM_PCM0_MEM_TO_DEV,
+ DMA_DEVICE_APM_PCM1_DEV_TO_MEM,
+ DMA_DEVICE_APM_PCM1_MEM_TO_DEV,
+ DMA_DEVICE_SPUM_DEV_TO_MEM,
+ DMA_DEVICE_SPUM_MEM_TO_DEV,
+ DMA_DEVICE_SPIH_DEV_TO_MEM,
+ DMA_DEVICE_SPIH_MEM_TO_DEV,
+ DMA_DEVICE_UART_A_DEV_TO_MEM,
+ DMA_DEVICE_UART_A_MEM_TO_DEV,
+ DMA_DEVICE_UART_B_DEV_TO_MEM,
+ DMA_DEVICE_UART_B_MEM_TO_DEV,
+ DMA_DEVICE_PIF_MEM_TO_DEV,
+ DMA_DEVICE_PIF_DEV_TO_MEM,
+ DMA_DEVICE_ESW_DEV_TO_MEM,
+ DMA_DEVICE_ESW_MEM_TO_DEV,
+ DMA_DEVICE_VPM_MEM_TO_MEM,
+ DMA_DEVICE_CLCD_MEM_TO_MEM,
+ DMA_DEVICE_NAND_MEM_TO_MEM,
+ DMA_DEVICE_MEM_TO_VRAM,
+ DMA_DEVICE_VRAM_TO_MEM,
+
+ /* Add new entries before this line. */
+
+ DMA_NUM_DEVICE_ENTRIES,
+ DMA_DEVICE_NONE = 0xff, /* Special value to indicate that no device is currently assigned. */
+
+} DMA_Device_t;
+
+/****************************************************************************
+*
+* The DMA_Handle_t is the primary object used by callers of the API.
+*
+*****************************************************************************/
+
+#define DMA_INVALID_HANDLE ((DMA_Handle_t) -1)
+
+typedef int DMA_Handle_t;
+
+/****************************************************************************
+*
+* The DMA_DescriptorRing_t contains a ring of descriptors which is used
+* to point to regions of memory.
+*
+*****************************************************************************/
+
+typedef struct {
+ void *virtAddr; /* Virtual Address of the descriptor ring */
+ dma_addr_t physAddr; /* Physical address of the descriptor ring */
+ int descriptorsAllocated; /* Number of descriptors allocated in the descriptor ring */
+ size_t bytesAllocated; /* Number of bytes allocated in the descriptor ring */
+
+} DMA_DescriptorRing_t;
+
+/****************************************************************************
+*
+* The DMA_MemType_t and DMA_MemMap_t are helper structures used to setup
+* DMA chains from a variety of memory sources.
+*
+*****************************************************************************/
+
+#define DMA_MEM_MAP_MIN_SIZE 4096 /* Pages less than this size are better */
+ /* off not being DMA'd. */
+
+typedef enum {
+ DMA_MEM_TYPE_NONE, /* Not a valid setting */
+ DMA_MEM_TYPE_VMALLOC, /* Memory came from vmalloc call */
+ DMA_MEM_TYPE_KMALLOC, /* Memory came from kmalloc call */
+ DMA_MEM_TYPE_DMA, /* Memory came from dma_alloc_xxx call */
+ DMA_MEM_TYPE_USER, /* Memory came from user space. */
+
+} DMA_MemType_t;
+
+/* A segment represents a physically and virtually contiguous chunk of memory. */
+/* i.e. each segment can be DMA'd */
+/* A user of the DMA code will add memory regions. Each region may need to be */
+/* represented by one or more segments. */
+
+typedef struct {
+ void *virtAddr; /* Virtual address used for this segment */
+ dma_addr_t physAddr; /* Physical address this segment maps to */
+ size_t numBytes; /* Size of the segment, in bytes */
+
+} DMA_Segment_t;
+
+/* A region represents a virtually contiguous chunk of memory, which may be */
+/* made up of multiple segments. */
+
+typedef struct {
+ DMA_MemType_t memType;
+ void *virtAddr;
+ size_t numBytes;
+
+ /* Each region (virtually contiguous) consists of one or more segments. Each */
+ /* segment is virtually and physically contiguous. */
+
+ int numSegmentsUsed;
+ int numSegmentsAllocated;
+ DMA_Segment_t *segment;
+
+ /* When a region corresponds to user memory, we need to lock all of the pages */
+ /* down before we can figure out the physical addresses. The lockedPage array contains */
+ /* the pages that were locked, and which subsequently need to be unlocked once the */
+ /* memory is unmapped. */
+
+ unsigned numLockedPages;
+ struct page **lockedPages;
+
+} DMA_Region_t;
+
+typedef struct {
+ int inUse; /* Is this mapping currently being used? */
+ struct semaphore lock; /* Acquired when using this structure */
+ enum dma_data_direction dir; /* Direction this transfer is intended for */
+
+ /* In the event that we're mapping user memory, we need to know which task */
+ /* the memory is for, so that we can obtain the correct mm locks. */
+
+ struct task_struct *userTask;
+
+ int numRegionsUsed;
+ int numRegionsAllocated;
+ DMA_Region_t *region;
+
+} DMA_MemMap_t;
+
+/****************************************************************************
+*
+* The DMA_DeviceAttribute_t contains information which describes a
+* particular DMA device (or peripheral).
+*
+* It is anticipated that the arrary of DMA_DeviceAttribute_t's will be
+* statically initialized.
+*
+*****************************************************************************/
+
+/* The device handler is called whenever a DMA operation completes. The reaon */
+/* for it to be called will be a bitmask with one or more of the following bits */
+/* set. */
+
+#define DMA_HANDLER_REASON_BLOCK_COMPLETE dmacHw_INTERRUPT_STATUS_BLOCK
+#define DMA_HANDLER_REASON_TRANSFER_COMPLETE dmacHw_INTERRUPT_STATUS_TRANS
+#define DMA_HANDLER_REASON_ERROR dmacHw_INTERRUPT_STATUS_ERROR
+
+typedef void (*DMA_DeviceHandler_t) (DMA_Device_t dev, int reason,
+ void *userData);
+
+#define DMA_DEVICE_FLAG_ON_DMA0 0x00000001
+#define DMA_DEVICE_FLAG_ON_DMA1 0x00000002
+#define DMA_DEVICE_FLAG_PORT_PER_DMAC 0x00000004 /* If set, it means that the port used on DMAC0 is different from the port used on DMAC1 */
+#define DMA_DEVICE_FLAG_ALLOC_DMA1_FIRST 0x00000008 /* If set, allocate from DMA1 before allocating from DMA0 */
+#define DMA_DEVICE_FLAG_IS_DEDICATED 0x00000100
+#define DMA_DEVICE_FLAG_NO_ISR 0x00000200
+#define DMA_DEVICE_FLAG_ALLOW_LARGE_FIFO 0x00000400
+#define DMA_DEVICE_FLAG_IN_USE 0x00000800 /* If set, device is in use on a channel */
+
+/* Note: Some DMA devices can be used from multiple DMA Controllers. The bitmask is used to */
+/* determine which DMA controllers a given device can be used from, and the interface */
+/* array determeines the actual interface number to use for a given controller. */
+
+typedef struct {
+ uint32_t flags; /* Bitmask of DMA_DEVICE_FLAG_xxx constants */
+ uint8_t dedicatedController; /* Controller number to use if DMA_DEVICE_FLAG_IS_DEDICATED is set. */
+ uint8_t dedicatedChannel; /* Channel number to use if DMA_DEVICE_FLAG_IS_DEDICATED is set. */
+ const char *name; /* Will show up in the /proc entry */
+
+ uint32_t dmacPort[DMA_NUM_CONTROLLERS]; /* Specifies the port number when DMA_DEVICE_FLAG_PORT_PER_DMAC flag is set */
+
+ dmacHw_CONFIG_t config; /* Configuration to use when DMA'ing using this device */
+
+ void *userData; /* Passed to the devHandler */
+ DMA_DeviceHandler_t devHandler; /* Called when DMA operations finish. */
+
+ timer_tick_count_t transferStartTime; /* Time the current transfer was started */
+
+ /* The following statistical information will be collected and presented in a proc entry. */
+ /* Note: With a contiuous bandwidth of 1 Gb/sec, it would take 584 years to overflow */
+ /* a 64 bit counter. */
+
+ uint64_t numTransfers; /* Number of DMA transfers performed */
+ uint64_t transferTicks; /* Total time spent doing DMA transfers (measured in timer_tick_count_t's) */
+ uint64_t transferBytes; /* Total bytes transferred */
+ uint32_t timesBlocked; /* Number of times a channel was unavailable */
+ uint32_t numBytes; /* Last transfer size */
+
+ /* It's not possible to free memory which is allocated for the descriptors from within */
+ /* the ISR. So make the presumption that a given device will tend to use the */
+ /* same sized buffers over and over again, and we keep them around. */
+
+ DMA_DescriptorRing_t ring; /* Ring of descriptors allocated for this device */
+
+ /* We stash away some of the information from the previous transfer. If back-to-back */
+ /* transfers are performed from the same buffer, then we don't have to keep re-initializing */
+ /* the descriptor buffers. */
+
+ uint32_t prevNumBytes;
+ dma_addr_t prevSrcData;
+ dma_addr_t prevDstData;
+
+} DMA_DeviceAttribute_t;
+
+/****************************************************************************
+*
+* DMA_Channel_t, DMA_Controller_t, and DMA_State_t are really internal
+* data structures and don't belong in this header file, but are included
+* merely for discussion.
+*
+* By the time this is implemented, these structures will be moved out into
+* the appropriate C source file instead.
+*
+*****************************************************************************/
+
+/****************************************************************************
+*
+* The DMA_Channel_t contains state information about each DMA channel. Some
+* of the channels are dedicated. Non-dedicated channels are shared
+* amongst the other devices.
+*
+*****************************************************************************/
+
+#define DMA_CHANNEL_FLAG_IN_USE 0x00000001
+#define DMA_CHANNEL_FLAG_IS_DEDICATED 0x00000002
+#define DMA_CHANNEL_FLAG_NO_ISR 0x00000004
+#define DMA_CHANNEL_FLAG_LARGE_FIFO 0x00000008
+
+typedef struct {
+ uint32_t flags; /* bitmask of DMA_CHANNEL_FLAG_xxx constants */
+ DMA_Device_t devType; /* Device this channel is currently reserved for */
+ DMA_Device_t lastDevType; /* Device type that used this previously */
+ char name[20]; /* Name passed onto request_irq */
+
+#if (DMA_DEBUG_TRACK_RESERVATION)
+ const char *fileName; /* Place where channel reservation took place */
+ int lineNum; /* Place where channel reservation took place */
+#endif
+ dmacHw_HANDLE_t dmacHwHandle; /* low level channel handle. */
+
+} DMA_Channel_t;
+
+/****************************************************************************
+*
+* The DMA_Controller_t contains state information about each DMA controller.
+*
+* The freeChannelQ is stored in the controller data structure rather than
+* the channel data structure since several of the devices are accessible
+* from multiple controllers, and there is no way to know which controller
+* will become available first.
+*
+*****************************************************************************/
+
+typedef struct {
+ DMA_Channel_t channel[DMA_NUM_CHANNELS];
+
+} DMA_Controller_t;
+
+/****************************************************************************
+*
+* The DMA_Global_t contains all of the global state information used by
+* the DMA code.
+*
+* Callers which need to allocate a shared channel will be queued up
+* on the freeChannelQ until a channel becomes available.
+*
+*****************************************************************************/
+
+typedef struct {
+ struct semaphore lock; /* acquired when manipulating table entries */
+ wait_queue_head_t freeChannelQ;
+
+ DMA_Controller_t controller[DMA_NUM_CONTROLLERS];
+
+} DMA_Global_t;
+
+/* ---- Variable Externs ------------------------------------------------- */
+
+extern DMA_DeviceAttribute_t DMA_gDeviceAttribute[DMA_NUM_DEVICE_ENTRIES];
+
+/* ---- Function Prototypes ---------------------------------------------- */
+
+#if defined(__KERNEL__)
+
+/****************************************************************************/
+/**
+* Initializes the DMA module.
+*
+* @return
+* 0 - Success
+* < 0 - Error
+*/
+/****************************************************************************/
+
+int dma_init(void);
+
+#if (DMA_DEBUG_TRACK_RESERVATION)
+DMA_Handle_t dma_request_channel_dbg(DMA_Device_t dev, const char *fileName,
+ int lineNum);
+#define dma_request_channel(dev) dma_request_channel_dbg(dev, __FILE__, __LINE__)
+#else
+
+/****************************************************************************/
+/**
+* Reserves a channel for use with @a dev. If the device is setup to use
+* a shared channel, then this function will block until a free channel
+* becomes available.
+*
+* @return
+* >= 0 - A valid DMA Handle.
+* -EBUSY - Device is currently being used.
+* -ENODEV - Device handed in is invalid.
+*/
+/****************************************************************************/
+
+DMA_Handle_t dma_request_channel(DMA_Device_t dev /* Device to use with the allocated channel. */
+ );
+#endif
+
+/****************************************************************************/
+/**
+* Frees a previously allocated DMA Handle.
+*
+* @return
+* 0 - DMA Handle was released successfully.
+* -EINVAL - Invalid DMA handle
+*/
+/****************************************************************************/
+
+int dma_free_channel(DMA_Handle_t channel /* DMA handle. */
+ );
+
+/****************************************************************************/
+/**
+* Determines if a given device has been configured as using a shared
+* channel.
+*
+* @return boolean
+* 0 Device uses a dedicated channel
+* non-zero Device uses a shared channel
+*/
+/****************************************************************************/
+
+int dma_device_is_channel_shared(DMA_Device_t dev /* Device to check. */
+ );
+
+/****************************************************************************/
+/**
+* Allocates memory to hold a descriptor ring. The descriptor ring then
+* needs to be populated by making one or more calls to
+* dna_add_descriptors.
+*
+* The returned descriptor ring will be automatically initialized.
+*
+* @return
+* 0 Descriptor ring was allocated successfully
+* -ENOMEM Unable to allocate memory for the desired number of descriptors.
+*/
+/****************************************************************************/
+
+int dma_alloc_descriptor_ring(DMA_DescriptorRing_t *ring, /* Descriptor ring to populate */
+ int numDescriptors /* Number of descriptors that need to be allocated. */
+ );
+
+/****************************************************************************/
+/**
+* Releases the memory which was previously allocated for a descriptor ring.
+*/
+/****************************************************************************/
+
+void dma_free_descriptor_ring(DMA_DescriptorRing_t *ring /* Descriptor to release */
+ );
+
+/****************************************************************************/
+/**
+* Initializes a descriptor ring, so that descriptors can be added to it.
+* Once a descriptor ring has been allocated, it may be reinitialized for
+* use with additional/different regions of memory.
+*
+* Note that if 7 descriptors are allocated, it's perfectly acceptable to
+* initialize the ring with a smaller number of descriptors. The amount
+* of memory allocated for the descriptor ring will not be reduced, and
+* the descriptor ring may be reinitialized later
+*
+* @return
+* 0 Descriptor ring was initialized successfully
+* -ENOMEM The descriptor which was passed in has insufficient space
+* to hold the desired number of descriptors.
+*/
+/****************************************************************************/
+
+int dma_init_descriptor_ring(DMA_DescriptorRing_t *ring, /* Descriptor ring to initialize */
+ int numDescriptors /* Number of descriptors to initialize. */
+ );
+
+/****************************************************************************/
+/**
+* Determines the number of descriptors which would be required for a
+* transfer of the indicated memory region.
+*
+* This function also needs to know which DMA device this transfer will
+* be destined for, so that the appropriate DMA configuration can be retrieved.
+* DMA parameters such as transfer width, and whether this is a memory-to-memory
+* or memory-to-peripheral, etc can all affect the actual number of descriptors
+* required.
+*
+* @return
+* > 0 Returns the number of descriptors required for the indicated transfer
+* -EINVAL Invalid device type for this kind of transfer
+* (i.e. the device is _MEM_TO_DEV and not _DEV_TO_MEM)
+* -ENOMEM Memory exhausted
+*/
+/****************************************************************************/
+
+int dma_calculate_descriptor_count(DMA_Device_t device, /* DMA Device that this will be associated with */
+ dma_addr_t srcData, /* Place to get data to write to device */
+ dma_addr_t dstData, /* Pointer to device data address */
+ size_t numBytes /* Number of bytes to transfer to the device */
+ );
+
+/****************************************************************************/
+/**
+* Adds a region of memory to the descriptor ring. Note that it may take
+* multiple descriptors for each region of memory. It is the callers
+* responsibility to allocate a sufficiently large descriptor ring.
+*
+* @return
+* 0 Descriptors were added successfully
+* -EINVAL Invalid device type for this kind of transfer
+* (i.e. the device is _MEM_TO_DEV and not _DEV_TO_MEM)
+* -ENOMEM Memory exhausted
+*/
+/****************************************************************************/
+
+int dma_add_descriptors(DMA_DescriptorRing_t *ring, /* Descriptor ring to add descriptors to */
+ DMA_Device_t device, /* DMA Device that descriptors are for */
+ dma_addr_t srcData, /* Place to get data (memory or device) */
+ dma_addr_t dstData, /* Place to put data (memory or device) */
+ size_t numBytes /* Number of bytes to transfer to the device */
+ );
+
+/****************************************************************************/
+/**
+* Sets the descriptor ring associated with a device.
+*
+* Once set, the descriptor ring will be associated with the device, even
+* across channel request/free calls. Passing in a NULL descriptor ring
+* will release any descriptor ring currently associated with the device.
+*
+* Note: If you call dma_transfer, or one of the other dma_alloc_ functions
+* the descriptor ring may be released and reallocated.
+*
+* Note: This function will release the descriptor memory for any current
+* descriptor ring associated with this device.
+*/
+/****************************************************************************/
+
+int dma_set_device_descriptor_ring(DMA_Device_t device, /* Device to update the descriptor ring for. */
+ DMA_DescriptorRing_t *ring /* Descriptor ring to add descriptors to */
+ );
+
+/****************************************************************************/
+/**
+* Retrieves the descriptor ring associated with a device.
+*/
+/****************************************************************************/
+
+int dma_get_device_descriptor_ring(DMA_Device_t device, /* Device to retrieve the descriptor ring for. */
+ DMA_DescriptorRing_t *ring /* Place to store retrieved ring */
+ );
+
+/****************************************************************************/
+/**
+* Allocates buffers for the descriptors. This is normally done automatically
+* but needs to be done explicitly when initiating a dma from interrupt
+* context.
+*
+* @return
+* 0 Descriptors were allocated successfully
+* -EINVAL Invalid device type for this kind of transfer
+* (i.e. the device is _MEM_TO_DEV and not _DEV_TO_MEM)
+* -ENOMEM Memory exhausted
+*/
+/****************************************************************************/
+
+int dma_alloc_descriptors(DMA_Handle_t handle, /* DMA Handle */
+ dmacHw_TRANSFER_TYPE_e transferType, /* Type of transfer being performed */
+ dma_addr_t srcData, /* Place to get data to write to device */
+ dma_addr_t dstData, /* Pointer to device data address */
+ size_t numBytes /* Number of bytes to transfer to the device */
+ );
+
+/****************************************************************************/
+/**
+* Allocates and sets up descriptors for a double buffered circular buffer.
+*
+* This is primarily intended to be used for things like the ingress samples
+* from a microphone.
+*
+* @return
+* > 0 Number of descriptors actually allocated.
+* -EINVAL Invalid device type for this kind of transfer
+* (i.e. the device is _MEM_TO_DEV and not _DEV_TO_MEM)
+* -ENOMEM Memory exhausted
+*/
+/****************************************************************************/
+
+int dma_alloc_double_dst_descriptors(DMA_Handle_t handle, /* DMA Handle */
+ dma_addr_t srcData, /* Physical address of source data */
+ dma_addr_t dstData1, /* Physical address of first destination buffer */
+ dma_addr_t dstData2, /* Physical address of second destination buffer */
+ size_t numBytes /* Number of bytes in each destination buffer */
+ );
+
+/****************************************************************************/
+/**
+* Initializes a DMA_MemMap_t data structure
+*/
+/****************************************************************************/
+
+int dma_init_mem_map(DMA_MemMap_t *memMap /* Stores state information about the map */
+ );
+
+/****************************************************************************/
+/**
+* Releases any memory currently being held by a memory mapping structure.
+*/
+/****************************************************************************/
+
+int dma_term_mem_map(DMA_MemMap_t *memMap /* Stores state information about the map */
+ );
+
+/****************************************************************************/
+/**
+* Looks at a memory address and categorizes it.
+*
+* @return One of the values from the DMA_MemType_t enumeration.
+*/
+/****************************************************************************/
+
+DMA_MemType_t dma_mem_type(void *addr);
+
+/****************************************************************************/
+/**
+* Sets the process (aka userTask) associated with a mem map. This is
+* required if user-mode segments will be added to the mapping.
+*/
+/****************************************************************************/
+
+static inline void dma_mem_map_set_user_task(DMA_MemMap_t *memMap,
+ struct task_struct *task)
+{
+ memMap->userTask = task;
+}
+
+/****************************************************************************/
+/**
+* Looks at a memory address and determines if we support DMA'ing to/from
+* that type of memory.
+*
+* @return boolean -
+* return value != 0 means dma supported
+* return value == 0 means dma not supported
+*/
+/****************************************************************************/
+
+int dma_mem_supports_dma(void *addr);
+
+/****************************************************************************/
+/**
+* Initializes a memory map for use. Since this function acquires a
+* sempaphore within the memory map, it is VERY important that dma_unmap
+* be called when you're finished using the map.
+*/
+/****************************************************************************/
+
+int dma_map_start(DMA_MemMap_t *memMap, /* Stores state information about the map */
+ enum dma_data_direction dir /* Direction that the mapping will be going */
+ );
+
+/****************************************************************************/
+/**
+* Adds a segment of memory to a memory map.
+*
+* @return 0 on success, error code otherwise.
+*/
+/****************************************************************************/
+
+int dma_map_add_region(DMA_MemMap_t *memMap, /* Stores state information about the map */
+ void *mem, /* Virtual address that we want to get a map of */
+ size_t numBytes /* Number of bytes being mapped */
+ );
+
+/****************************************************************************/
+/**
+* Creates a descriptor ring from a memory mapping.
+*
+* @return 0 on sucess, error code otherwise.
+*/
+/****************************************************************************/
+
+int dma_map_create_descriptor_ring(DMA_Device_t dev, /* DMA device (where the ring is stored) */
+ DMA_MemMap_t *memMap, /* Memory map that will be used */
+ dma_addr_t devPhysAddr /* Physical address of device */
+ );
+
+/****************************************************************************/
+/**
+* Maps in a memory region such that it can be used for performing a DMA.
+*
+* @return
+*/
+/****************************************************************************/
+
+int dma_map_mem(DMA_MemMap_t *memMap, /* Stores state information about the map */
+ void *addr, /* Virtual address that we want to get a map of */
+ size_t count, /* Number of bytes being mapped */
+ enum dma_data_direction dir /* Direction that the mapping will be going */
+ );
+
+/****************************************************************************/
+/**
+* Maps in a memory region such that it can be used for performing a DMA.
+*
+* @return
+*/
+/****************************************************************************/
+
+int dma_unmap(DMA_MemMap_t *memMap, /* Stores state information about the map */
+ int dirtied /* non-zero if any of the pages were modified */
+ );
+
+/****************************************************************************/
+/**
+* Initiates a transfer when the descriptors have already been setup.
+*
+* This is a special case, and normally, the dma_transfer_xxx functions should
+* be used.
+*
+* @return
+* 0 Transfer was started successfully
+* -ENODEV Invalid handle
+*/
+/****************************************************************************/
+
+int dma_start_transfer(DMA_Handle_t handle);
+
+/****************************************************************************/
+/**
+* Stops a previously started DMA transfer.
+*
+* @return
+* 0 Transfer was stopped successfully
+* -ENODEV Invalid handle
+*/
+/****************************************************************************/
+
+int dma_stop_transfer(DMA_Handle_t handle);
+
+/****************************************************************************/
+/**
+* Waits for a DMA to complete by polling. This function is only intended
+* to be used for testing. Interrupts should be used for most DMA operations.
+*/
+/****************************************************************************/
+
+int dma_wait_transfer_done(DMA_Handle_t handle);
+
+/****************************************************************************/
+/**
+* Initiates a DMA transfer
+*
+* @return
+* 0 Transfer was started successfully
+* -EINVAL Invalid device type for this kind of transfer
+* (i.e. the device is _MEM_TO_DEV and not _DEV_TO_MEM)
+*/
+/****************************************************************************/
+
+int dma_transfer(DMA_Handle_t handle, /* DMA Handle */
+ dmacHw_TRANSFER_TYPE_e transferType, /* Type of transfer being performed */
+ dma_addr_t srcData, /* Place to get data to write to device */
+ dma_addr_t dstData, /* Pointer to device data address */
+ size_t numBytes /* Number of bytes to transfer to the device */
+ );
+
+/****************************************************************************/
+/**
+* Initiates a transfer from memory to a device.
+*
+* @return
+* 0 Transfer was started successfully
+* -EINVAL Invalid device type for this kind of transfer
+* (i.e. the device is _DEV_TO_MEM and not _MEM_TO_DEV)
+*/
+/****************************************************************************/
+
+static inline int dma_transfer_to_device(DMA_Handle_t handle, /* DMA Handle */
+ dma_addr_t srcData, /* Place to get data to write to device (physical address) */
+ dma_addr_t dstData, /* Pointer to device data address (physical address) */
+ size_t numBytes /* Number of bytes to transfer to the device */
+ ) {
+ return dma_transfer(handle,
+ dmacHw_TRANSFER_TYPE_MEM_TO_PERIPHERAL,
+ srcData, dstData, numBytes);
+}
+
+/****************************************************************************/
+/**
+* Initiates a transfer from a device to memory.
+*
+* @return
+* 0 Transfer was started successfully
+* -EINVAL Invalid device type for this kind of transfer
+* (i.e. the device is _MEM_TO_DEV and not _DEV_TO_MEM)
+*/
+/****************************************************************************/
+
+static inline int dma_transfer_from_device(DMA_Handle_t handle, /* DMA Handle */
+ dma_addr_t srcData, /* Pointer to the device data address (physical address) */
+ dma_addr_t dstData, /* Place to store data retrieved from the device (physical address) */
+ size_t numBytes /* Number of bytes to retrieve from the device */
+ ) {
+ return dma_transfer(handle,
+ dmacHw_TRANSFER_TYPE_PERIPHERAL_TO_MEM,
+ srcData, dstData, numBytes);
+}
+
+/****************************************************************************/
+/**
+* Initiates a memory to memory transfer.
+*
+* @return
+* 0 Transfer was started successfully
+* -EINVAL Invalid device type for this kind of transfer
+* (i.e. the device wasn't DMA_DEVICE_MEM_TO_MEM)
+*/
+/****************************************************************************/
+
+static inline int dma_transfer_mem_to_mem(DMA_Handle_t handle, /* DMA Handle */
+ dma_addr_t srcData, /* Place to transfer data from (physical address) */
+ dma_addr_t dstData, /* Place to transfer data to (physical address) */
+ size_t numBytes /* Number of bytes to transfer */
+ ) {
+ return dma_transfer(handle,
+ dmacHw_TRANSFER_TYPE_MEM_TO_MEM,
+ srcData, dstData, numBytes);
+}
+
+/****************************************************************************/
+/**
+* Set the callback function which will be called when a transfer completes.
+* If a NULL callback function is set, then no callback will occur.
+*
+* @note @a devHandler will be called from IRQ context.
+*
+* @return
+* 0 - Success
+* -ENODEV - Device handed in is invalid.
+*/
+/****************************************************************************/
+
+int dma_set_device_handler(DMA_Device_t dev, /* Device to set the callback for. */
+ DMA_DeviceHandler_t devHandler, /* Function to call when the DMA completes */
+ void *userData /* Pointer which will be passed to devHandler. */
+ );
+
+#endif
+
+#endif /* ASM_ARM_ARCH_BCMRING_DMA_H */
diff --git a/arch/arm/mach-bcmring/include/mach/entry-macro.S b/arch/arm/mach-bcmring/include/mach/entry-macro.S
new file mode 100644
index 00000000000..7d393ca010a
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/entry-macro.S
@@ -0,0 +1,86 @@
+/*****************************************************************************
+* Copyright 2006 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/*
+ *
+ * Low-level IRQ helper macros for BCMRing-based platforms
+ *
+ */
+#include <mach/irqs.h>
+#include <mach/hardware.h>
+#include <mach/csp/mm_io.h>
+
+ .macro disable_fiq
+ .endm
+
+ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
+ ldr \base, =(MM_IO_BASE_INTC0)
+ ldr \irqstat, [\base, #0] @ get status
+ ldr \irqnr, [\base, #0x10] @ mask with enable register
+ ands \irqstat, \irqstat, \irqnr
+ mov \irqnr, #IRQ_INTC0_START
+ cmp \irqstat, #0
+ bne 1001f
+
+ ldr \base, =(MM_IO_BASE_INTC1)
+ ldr \irqstat, [\base, #0] @ get status
+ ldr \irqnr, [\base, #0x10] @ mask with enable register
+ ands \irqstat, \irqstat, \irqnr
+ mov \irqnr, #IRQ_INTC1_START
+ cmp \irqstat, #0
+ bne 1001f
+
+ ldr \base, =(MM_IO_BASE_SINTC)
+ ldr \irqstat, [\base, #0] @ get status
+ ldr \irqnr, [\base, #0x10] @ mask with enable register
+ ands \irqstat, \irqstat, \irqnr
+ mov \irqnr, #0xffffffff @ code meaning no interrupt bits set
+ cmp \irqstat, #0
+ beq 1002f
+
+ mov \irqnr, #IRQ_SINTC_START @ something is set, so fixup return value
+
+1001:
+ movs \tmp, \irqstat, lsl #16
+ movne \irqstat, \tmp
+ addeq \irqnr, \irqnr, #16
+
+ movs \tmp, \irqstat, lsl #8
+ movne \irqstat, \tmp
+ addeq \irqnr, \irqnr, #8
+
+ movs \tmp, \irqstat, lsl #4
+ movne \irqstat, \tmp
+ addeq \irqnr, \irqnr, #4
+
+ movs \tmp, \irqstat, lsl #2
+ movne \irqstat, \tmp
+ addeq \irqnr, \irqnr, #2
+
+ movs \tmp, \irqstat, lsl #1
+ addeq \irqnr, \irqnr, #1
+ orrs \base, \base, #1
+
+1002: @ irqnr will be set to 0xffffffff if no irq bits are set
+ .endm
+
+ .macro get_irqnr_preamble, base, tmp
+ .endm
+
+ .macro arch_ret_to_user, tmp1, tmp2
+ .endm
+
+ .macro irq_prio_table
+ .endm
+
diff --git a/arch/arm/mach-bcmring/include/mach/hardware.h b/arch/arm/mach-bcmring/include/mach/hardware.h
new file mode 100644
index 00000000000..447eb340c61
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/hardware.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * This file contains the hardware definitions of the BCMRing.
+ *
+ * Copyright (C) 1999 ARM Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_HARDWARE_H
+#define __ASM_ARCH_HARDWARE_H
+
+#include <asm/sizes.h>
+#include <mach/memory.h>
+#include <cfg_global.h>
+#include <mach/csp/mm_io.h>
+
+/* Hardware addresses of major areas.
+ * *_START is the physical address
+ * *_SIZE is the size of the region
+ * *_BASE is the virtual address
+ */
+#define RAM_START PHYS_OFFSET
+
+#define RAM_SIZE (CFG_GLOBAL_RAM_SIZE-CFG_GLOBAL_RAM_SIZE_RESERVED)
+#define RAM_BASE PAGE_OFFSET
+
+#define pcibios_assign_all_busses() 1
+
+/* Macros to make managing spinlocks a bit more controlled in terms of naming. */
+/* See reg_gpio.h, reg_irq.h, arch.c, gpio.c for example usage. */
+#if defined(__KERNEL__)
+#define HW_DECLARE_SPINLOCK(name) DEFINE_SPINLOCK(bcmring_##name##_reg_lock);
+#define HW_EXTERN_SPINLOCK(name) extern spinlock_t bcmring_##name##_reg_lock;
+#define HW_IRQ_SAVE(name, val) spin_lock_irqsave(&bcmring_##name##_reg_lock, (val))
+#define HW_IRQ_RESTORE(name, val) spin_unlock_irqrestore(&bcmring_##name##_reg_lock, (val))
+#else
+#define HW_DECLARE_SPINLOCK(name)
+#define HW_EXTERN_SPINLOCK(name)
+#define HW_IRQ_SAVE(name, val) {(void)(name); (void)(val); }
+#define HW_IRQ_RESTORE(name, val) {(void)(name); (void)(val); }
+#endif
+
+#ifndef HW_IO_PHYS_TO_VIRT
+#define HW_IO_PHYS_TO_VIRT MM_IO_PHYS_TO_VIRT
+#endif
+#define HW_IO_VIRT_TO_PHYS MM_IO_VIRT_TO_PHYS
+
+#endif
diff --git a/arch/arm/mach-bcmring/include/mach/io.h b/arch/arm/mach-bcmring/include/mach/io.h
new file mode 100644
index 00000000000..4db0eff9035
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/io.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright (C) 1999 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#include <mach/hardware.h>
+
+#define IO_SPACE_LIMIT 0xffffffff
+
+#define __io(a) ((void __iomem *)HW_IO_PHYS_TO_VIRT(a))
+
+/* Do not enable mem_pci for a big endian arm architecture or unexpected byteswaps will */
+/* happen in readw/writew etc. */
+
+#define readb(c) __raw_readb(c)
+#define readw(c) __raw_readw(c)
+#define readl(c) __raw_readl(c)
+#define readb_relaxed(addr) readb(addr)
+#define readw_relaxed(addr) readw(addr)
+#define readl_relaxed(addr) readl(addr)
+
+#define readsb(p, d, l) __raw_readsb(p, d, l)
+#define readsw(p, d, l) __raw_readsw(p, d, l)
+#define readsl(p, d, l) __raw_readsl(p, d, l)
+
+#define writeb(v, c) __raw_writeb(v, c)
+#define writew(v, c) __raw_writew(v, c)
+#define writel(v, c) __raw_writel(v, c)
+
+#define writesb(p, d, l) __raw_writesb(p, d, l)
+#define writesw(p, d, l) __raw_writesw(p, d, l)
+#define writesl(p, d, l) __raw_writesl(p, d, l)
+
+#define memset_io(c, v, l) _memset_io((c), (v), (l))
+#define memcpy_fromio(a, c, l) _memcpy_fromio((a), (c), (l))
+#define memcpy_toio(c, a, l) _memcpy_toio((c), (a), (l))
+
+#define eth_io_copy_and_sum(s, c, l, b) eth_copy_and_sum((s), (c), (l), (b))
+
+#endif
diff --git a/arch/arm/mach-bcmring/include/mach/irqs.h b/arch/arm/mach-bcmring/include/mach/irqs.h
new file mode 100644
index 00000000000..b279b825d4a
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/irqs.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2007 Broadcom
+ * Copyright (C) 1999 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if !defined(ARCH_BCMRING_IRQS_H)
+#define ARCH_BCMRING_IRQS_H
+
+/* INTC0 - interrupt controller 0 */
+#define IRQ_INTC0_START 0
+#define IRQ_DMA0C0 0 /* DMA0 channel 0 interrupt */
+#define IRQ_DMA0C1 1 /* DMA0 channel 1 interrupt */
+#define IRQ_DMA0C2 2 /* DMA0 channel 2 interrupt */
+#define IRQ_DMA0C3 3 /* DMA0 channel 3 interrupt */
+#define IRQ_DMA0C4 4 /* DMA0 channel 4 interrupt */
+#define IRQ_DMA0C5 5 /* DMA0 channel 5 interrupt */
+#define IRQ_DMA0C6 6 /* DMA0 channel 6 interrupt */
+#define IRQ_DMA0C7 7 /* DMA0 channel 7 interrupt */
+#define IRQ_DMA1C0 8 /* DMA1 channel 0 interrupt */
+#define IRQ_DMA1C1 9 /* DMA1 channel 1 interrupt */
+#define IRQ_DMA1C2 10 /* DMA1 channel 2 interrupt */
+#define IRQ_DMA1C3 11 /* DMA1 channel 3 interrupt */
+#define IRQ_DMA1C4 12 /* DMA1 channel 4 interrupt */
+#define IRQ_DMA1C5 13 /* DMA1 channel 5 interrupt */
+#define IRQ_DMA1C6 14 /* DMA1 channel 6 interrupt */
+#define IRQ_DMA1C7 15 /* DMA1 channel 7 interrupt */
+#define IRQ_VPM 16 /* Voice process module interrupt */
+#define IRQ_USBHD2 17 /* USB host2/device2 interrupt */
+#define IRQ_USBH1 18 /* USB1 host interrupt */
+#define IRQ_USBD 19 /* USB device interrupt */
+#define IRQ_SDIOH0 20 /* SDIO0 host interrupt */
+#define IRQ_SDIOH1 21 /* SDIO1 host interrupt */
+#define IRQ_TIMER0 22 /* Timer0 interrupt */
+#define IRQ_TIMER1 23 /* Timer1 interrupt */
+#define IRQ_TIMER2 24 /* Timer2 interrupt */
+#define IRQ_TIMER3 25 /* Timer3 interrupt */
+#define IRQ_SPIH 26 /* SPI host interrupt */
+#define IRQ_ESW 27 /* Ethernet switch interrupt */
+#define IRQ_APM 28 /* Audio process module interrupt */
+#define IRQ_GE 29 /* Graphic engine interrupt */
+#define IRQ_CLCD 30 /* LCD Controller interrupt */
+#define IRQ_PIF 31 /* Peripheral interface interrupt */
+#define IRQ_INTC0_END 31
+
+/* INTC1 - interrupt controller 1 */
+#define IRQ_INTC1_START 32
+#define IRQ_GPIO0 32 /* 0 GPIO bit 31//0 combined interrupt */
+#define IRQ_GPIO1 33 /* 1 GPIO bit 64//32 combined interrupt */
+#define IRQ_I2S0 34 /* 2 I2S0 interrupt */
+#define IRQ_I2S1 35 /* 3 I2S1 interrupt */
+#define IRQ_I2CH 36 /* 4 I2C host interrupt */
+#define IRQ_I2CS 37 /* 5 I2C slave interrupt */
+#define IRQ_SPIS 38 /* 6 SPI slave interrupt */
+#define IRQ_GPHY 39 /* 7 Gigabit Phy interrupt */
+#define IRQ_FLASHC 40 /* 8 Flash controller interrupt */
+#define IRQ_COMMTX 41 /* 9 ARM DDC transmit interrupt */
+#define IRQ_COMMRX 42 /* 10 ARM DDC receive interrupt */
+#define IRQ_PMUIRQ 43 /* 11 ARM performance monitor interrupt */
+#define IRQ_UARTB 44 /* 12 UARTB */
+#define IRQ_WATCHDOG 45 /* 13 Watchdog timer interrupt */
+#define IRQ_UARTA 46 /* 14 UARTA */
+#define IRQ_TSC 47 /* 15 Touch screen controller interrupt */
+#define IRQ_KEYC 48 /* 16 Key pad controller interrupt */
+#define IRQ_DMPU 49 /* 17 DDR2 memory partition interrupt */
+#define IRQ_VMPU 50 /* 18 VRAM memory partition interrupt */
+#define IRQ_FMPU 51 /* 19 Flash memory parition unit interrupt */
+#define IRQ_RNG 52 /* 20 Random number generator interrupt */
+#define IRQ_RTC0 53 /* 21 Real time clock periodic interrupt */
+#define IRQ_RTC1 54 /* 22 Real time clock one-shot interrupt */
+#define IRQ_SPUM 55 /* 23 Secure process module interrupt */
+#define IRQ_VDEC 56 /* 24 Hantro video decoder interrupt */
+#define IRQ_RTC2 57 /* 25 Real time clock tamper interrupt */
+#define IRQ_DDRP 58 /* 26 DDR Panic interrupt */
+#define IRQ_INTC1_END 58
+
+/* SINTC secure int controller */
+#define IRQ_SINTC_START 59
+#define IRQ_SEC_WATCHDOG 59 /* 0 Watchdog timer interrupt */
+#define IRQ_SEC_UARTA 60 /* 1 UARTA interrupt */
+#define IRQ_SEC_TSC 61 /* 2 Touch screen controller interrupt */
+#define IRQ_SEC_KEYC 62 /* 3 Key pad controller interrupt */
+#define IRQ_SEC_DMPU 63 /* 4 DDR2 memory partition interrupt */
+#define IRQ_SEC_VMPU 64 /* 5 VRAM memory partition interrupt */
+#define IRQ_SEC_FMPU 65 /* 6 Flash memory parition unit interrupt */
+#define IRQ_SEC_RNG 66 /* 7 Random number generator interrupt */
+#define IRQ_SEC_RTC0 67 /* 8 Real time clock periodic interrupt */
+#define IRQ_SEC_RTC1 68 /* 9 Real time clock one-shot interrupt */
+#define IRQ_SEC_SPUM 69 /* 10 Secure process module interrupt */
+#define IRQ_SEC_TIMER0 70 /* 11 Secure timer0 interrupt */
+#define IRQ_SEC_TIMER1 71 /* 12 Secure timer1 interrupt */
+#define IRQ_SEC_TIMER2 72 /* 13 Secure timer2 interrupt */
+#define IRQ_SEC_TIMER3 73 /* 14 Secure timer3 interrupt */
+#define IRQ_SEC_RTC2 74 /* 15 Real time clock tamper interrupt */
+
+#define IRQ_SINTC_END 74
+
+/* Note: there are 3 INTC registers of 32 bits each. So internal IRQs could go from 0-95 */
+/* Since IRQs are typically viewed in decimal, we start the gpio based IRQs off at 100 */
+/* to make the mapping easy for humans to decipher. */
+
+#define IRQ_GPIO_0 100
+
+#define NUM_INTERNAL_IRQS (IRQ_SINTC_END+1)
+
+/* I couldn't get the gpioHw_reg.h file to be included cleanly, so I hardcoded it */
+/* define NUM_GPIO_IRQS GPIOHW_TOTAL_NUM_PINS */
+#define NUM_GPIO_IRQS 62
+
+#define NR_IRQS (IRQ_GPIO_0 + NUM_GPIO_IRQS)
+
+#define IRQ_UNKNOWN -1
+
+/* Tune these bits to preclude noisy or unsupported interrupt sources as required. */
+#define IRQ_INTC0_VALID_MASK 0xffffffff
+#define IRQ_INTC1_VALID_MASK 0x07ffffff
+#define IRQ_SINTC_VALID_MASK 0x0000ffff
+
+#endif /* ARCH_BCMRING_IRQS_H */
diff --git a/arch/arm/mach-bcmring/include/mach/memory.h b/arch/arm/mach-bcmring/include/mach/memory.h
new file mode 100644
index 00000000000..114f942bb4f
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/memory.h
@@ -0,0 +1,33 @@
+/*****************************************************************************
+* Copyright 2005 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+#include <cfg_global.h>
+
+/*
+ * Physical vs virtual RAM address space conversion. These are
+ * private definitions which should NOT be used outside memory.h
+ * files. Use virt_to_phys/phys_to_virt/__pa/__va instead.
+ */
+
+#define PHYS_OFFSET CFG_GLOBAL_RAM_BASE
+
+/*
+ * Maximum DMA memory allowed is 14M
+ */
+#define CONSISTENT_DMA_SIZE (SZ_16M - SZ_2M)
+
+#endif
diff --git a/arch/arm/mach-bcmring/include/mach/memory_settings.h b/arch/arm/mach-bcmring/include/mach/memory_settings.h
new file mode 100644
index 00000000000..ce5cd16f2ac
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/memory_settings.h
@@ -0,0 +1,67 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#ifndef MEMORY_SETTINGS_H
+#define MEMORY_SETTINGS_H
+
+/* ---- Include Files ---------------------------------------- */
+/* ---- Constants and Types ---------------------------------- */
+
+/* Memory devices */
+/* NAND Flash timing for 166 MHz setting */
+#define HW_CFG_NAND_tBTA (5 << 16) /* Bus turnaround cycle (n) 0-7 (30 ns) */
+#define HW_CFG_NAND_tWP (4 << 11) /* Write pulse width cycle (n+1) 0-31 (25 ns) */
+#define HW_CFG_NAND_tWR (1 << 9) /* Write recovery cycle (n+1) 0-3 (10 ns) */
+#define HW_CFG_NAND_tAS (0 << 7) /* Write address setup cycle (n+1) 0-3 ( 0 ns) */
+#define HW_CFG_NAND_tOE (3 << 5) /* Output enable delay cycle (n) 0-3 (15 ns) */
+#define HW_CFG_NAND_tRC (7 << 0) /* Read access cycle (n+2) 0-31 (50 ns) */
+
+#define HW_CFG_NAND_TCR (HW_CFG_NAND_tBTA \
+ | HW_CFG_NAND_tWP \
+ | HW_CFG_NAND_tWR \
+ | HW_CFG_NAND_tAS \
+ | HW_CFG_NAND_tOE \
+ | HW_CFG_NAND_tRC)
+
+/* NOR Flash timing for 166 MHz setting */
+#define HW_CFG_NOR_TPRC_TWLC (0 << 19) /* Page read access cycle / Burst write latency (n+2 / n+1) (max 25ns) */
+#define HW_CFG_NOR_TBTA (0 << 16) /* Bus turnaround cycle (n) (DNA) */
+#define HW_CFG_NOR_TWP (6 << 11) /* Write pulse width cycle (n+1) (35ns) */
+#define HW_CFG_NOR_TWR (0 << 9) /* Write recovery cycle (n+1) (0ns) */
+#define HW_CFG_NOR_TAS (0 << 7) /* Write address setup cycle (n+1) (0ns) */
+#define HW_CFG_NOR_TOE (0 << 5) /* Output enable delay cycle (n) (max 25ns) */
+#define HW_CFG_NOR_TRC_TLC (0x10 << 0) /* Read access cycle / Burst read latency (n+2 / n+1) (100ns) */
+
+#define HW_CFG_FLASH0_TCR (HW_CFG_NOR_TPRC_TWLC \
+ | HW_CFG_NOR_TBTA \
+ | HW_CFG_NOR_TWP \
+ | HW_CFG_NOR_TWR \
+ | HW_CFG_NOR_TAS \
+ | HW_CFG_NOR_TOE \
+ | HW_CFG_NOR_TRC_TLC)
+
+#define HW_CFG_FLASH1_TCR HW_CFG_FLASH0_TCR
+#define HW_CFG_FLASH2_TCR HW_CFG_FLASH0_TCR
+
+/* SDRAM Settings */
+/* #define HW_CFG_SDRAM_CAS_LATENCY 5 Default 5, Values [3..6] */
+/* #define HW_CFG_SDRAM_CHIP_SELECT_CNT 1 Default 1, Vaules [1..2] */
+/* #define HW_CFG_SDRAM_SPEED_GRADE 667 Default 667, Values [400,533,667,800] */
+/* #define HW_CFG_SDRAM_WIDTH_BITS 16 Default 16, Vaules [8,16] */
+#define HW_CFG_SDRAM_SIZE_BYTES 0x10000000 /* Total memory, not per device size */
+
+/* ---- Variable Externs ------------------------------------- */
+/* ---- Function Prototypes ---------------------------------- */
+
+#endif /* MEMORY_SETTINGS_H */
diff --git a/arch/arm/mach-bcmring/include/mach/system.h b/arch/arm/mach-bcmring/include/mach/system.h
new file mode 100644
index 00000000000..cdbf93c694a
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/system.h
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright (C) 1999 ARM Limited
+ * Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_SYSTEM_H
+#define __ASM_ARCH_SYSTEM_H
+
+#include <mach/csp/chipcHw_inline.h>
+
+extern int bcmring_arch_warm_reboot;
+
+static inline void arch_idle(void)
+{
+ cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, char *cmd)
+{
+ printk("arch_reset:%c %x\n", mode, bcmring_arch_warm_reboot);
+
+ if (mode == 'h') {
+ /* Reboot configured in proc entry */
+ if (bcmring_arch_warm_reboot) {
+ printk("warm reset\n");
+ /* Issue Warm reset (do not reset ethernet switch, keep alive) */
+ chipcHw_reset(chipcHw_REG_SOFT_RESET_CHIP_WARM);
+ } else {
+ /* Force reset of everything */
+ printk("force reset\n");
+ chipcHw_reset(chipcHw_REG_SOFT_RESET_CHIP_SOFT);
+ }
+ } else {
+ /* Force reset of everything */
+ printk("force reset\n");
+ chipcHw_reset(chipcHw_REG_SOFT_RESET_CHIP_SOFT);
+ }
+}
+
+#endif
diff --git a/arch/arm/mach-bcmring/include/mach/timer.h b/arch/arm/mach-bcmring/include/mach/timer.h
new file mode 100644
index 00000000000..5a94bbb032b
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/timer.h
@@ -0,0 +1,77 @@
+/*****************************************************************************
+* Copyright 2004 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+/*
+*
+*****************************************************************************
+*
+* timer.h
+*
+* PURPOSE:
+*
+*
+*
+* NOTES:
+*
+*****************************************************************************/
+
+#if !defined(BCM_LINUX_TIMER_H)
+#define BCM_LINUX_TIMER_H
+
+#if defined(__KERNEL__)
+
+/* ---- Include Files ---------------------------------------------------- */
+/* ---- Constants and Types ---------------------------------------------- */
+
+typedef unsigned int timer_tick_count_t;
+typedef unsigned int timer_tick_rate_t;
+typedef unsigned int timer_msec_t;
+
+/* ---- Variable Externs ------------------------------------------------- */
+/* ---- Function Prototypes ---------------------------------------------- */
+
+/****************************************************************************
+*
+* timer_get_tick_count
+*
+*
+***************************************************************************/
+timer_tick_count_t timer_get_tick_count(void);
+
+/****************************************************************************
+*
+* timer_get_tick_rate
+*
+*
+***************************************************************************/
+timer_tick_rate_t timer_get_tick_rate(void);
+
+/****************************************************************************
+*
+* timer_get_msec
+*
+*
+***************************************************************************/
+timer_msec_t timer_get_msec(void);
+
+/****************************************************************************
+*
+* timer_ticks_to_msec
+*
+*
+***************************************************************************/
+timer_msec_t timer_ticks_to_msec(timer_tick_count_t ticks);
+
+#endif /* __KERNEL__ */
+#endif /* BCM_LINUX_TIMER_H */
diff --git a/arch/arm/mach-bcmring/include/mach/timex.h b/arch/arm/mach-bcmring/include/mach/timex.h
new file mode 100644
index 00000000000..40d033ec589
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/timex.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * Integrator architecture timex specifications
+ *
+ * Copyright (C) 1999 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Specifies the number of ticks per second
+ */
+#define CLOCK_TICK_RATE 100000 /* REG_SMT_TICKS_PER_SEC */
diff --git a/arch/arm/mach-bcmring/include/mach/uncompress.h b/arch/arm/mach-bcmring/include/mach/uncompress.h
new file mode 100644
index 00000000000..9c9821b7797
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/uncompress.h
@@ -0,0 +1,43 @@
+/*****************************************************************************
+* Copyright 2005 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+#include <mach/csp/mm_addr.h>
+
+#define BCMRING_UART_0_DR (*(volatile unsigned int *)MM_ADDR_IO_UARTA)
+#define BCMRING_UART_0_FR (*(volatile unsigned int *)(MM_ADDR_IO_UARTA + 0x18))
+/*
+ * This does not append a newline
+ */
+static inline void putc(int c)
+{
+ /* Send out UARTA */
+ while (BCMRING_UART_0_FR & (1 << 5))
+ ;
+
+ BCMRING_UART_0_DR = c;
+}
+
+
+static inline void flush(void)
+{
+ /* Wait for the tx fifo to be empty */
+ while ((BCMRING_UART_0_FR & (1 << 7)) == 0)
+ ;
+
+ /* Wait for the final character to be sent on the txd line */
+ while (BCMRING_UART_0_FR & (1 << 3))
+ ;
+}
+
+#define arch_decomp_setup()
+#define arch_decomp_wdog()
diff --git a/arch/arm/mach-bcmring/include/mach/vmalloc.h b/arch/arm/mach-bcmring/include/mach/vmalloc.h
new file mode 100644
index 00000000000..35e2ead8395
--- /dev/null
+++ b/arch/arm/mach-bcmring/include/mach/vmalloc.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * Copyright (C) 2000 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Move VMALLOC_END to 0xf0000000 so that the vm space can range from
+ * 0xe0000000 to 0xefffffff. This gives us 256 MB of vm space and handles
+ * larger physical memory designs better.
+ */
+#define VMALLOC_END (PAGE_OFFSET + 0x30000000)
diff --git a/arch/arm/mach-bcmring/irq.c b/arch/arm/mach-bcmring/irq.c
new file mode 100644
index 00000000000..dc1c4939b0c
--- /dev/null
+++ b/arch/arm/mach-bcmring/irq.c
@@ -0,0 +1,127 @@
+/*
+ *
+ * Copyright (C) 1999 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/init.h>
+#include <linux/stddef.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/version.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+
+#include <asm/mach/irq.h>
+#include <mach/csp/intcHw_reg.h>
+#include <mach/csp/mm_io.h>
+
+static void bcmring_mask_irq0(unsigned int irq)
+{
+ writel(1 << (irq - IRQ_INTC0_START),
+ MM_IO_BASE_INTC0 + INTCHW_INTENCLEAR);
+}
+
+static void bcmring_unmask_irq0(unsigned int irq)
+{
+ writel(1 << (irq - IRQ_INTC0_START),
+ MM_IO_BASE_INTC0 + INTCHW_INTENABLE);
+}
+
+static void bcmring_mask_irq1(unsigned int irq)
+{
+ writel(1 << (irq - IRQ_INTC1_START),
+ MM_IO_BASE_INTC1 + INTCHW_INTENCLEAR);
+}
+
+static void bcmring_unmask_irq1(unsigned int irq)
+{
+ writel(1 << (irq - IRQ_INTC1_START),
+ MM_IO_BASE_INTC1 + INTCHW_INTENABLE);
+}
+
+static void bcmring_mask_irq2(unsigned int irq)
+{
+ writel(1 << (irq - IRQ_SINTC_START),
+ MM_IO_BASE_SINTC + INTCHW_INTENCLEAR);
+}
+
+static void bcmring_unmask_irq2(unsigned int irq)
+{
+ writel(1 << (irq - IRQ_SINTC_START),
+ MM_IO_BASE_SINTC + INTCHW_INTENABLE);
+}
+
+static struct irq_chip bcmring_irq0_chip = {
+ .typename = "ARM-INTC0",
+ .ack = bcmring_mask_irq0,
+ .mask = bcmring_mask_irq0, /* mask a specific interrupt, blocking its delivery. */
+ .unmask = bcmring_unmask_irq0, /* unmaks an interrupt */
+};
+
+static struct irq_chip bcmring_irq1_chip = {
+ .typename = "ARM-INTC1",
+ .ack = bcmring_mask_irq1,
+ .mask = bcmring_mask_irq1,
+ .unmask = bcmring_unmask_irq1,
+};
+
+static struct irq_chip bcmring_irq2_chip = {
+ .typename = "ARM-SINTC",
+ .ack = bcmring_mask_irq2,
+ .mask = bcmring_mask_irq2,
+ .unmask = bcmring_unmask_irq2,
+};
+
+static void vic_init(void __iomem *base, struct irq_chip *chip,
+ unsigned int irq_start, unsigned int vic_sources)
+{
+ unsigned int i;
+ for (i = 0; i < 32; i++) {
+ unsigned int irq = irq_start + i;
+ set_irq_chip(irq, chip);
+ set_irq_chip_data(irq, base);
+
+ if (vic_sources & (1 << i)) {
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+ }
+ writel(0, base + INTCHW_INTSELECT);
+ writel(0, base + INTCHW_INTENABLE);
+ writel(~0, base + INTCHW_INTENCLEAR);
+ writel(0, base + INTCHW_IRQSTATUS);
+ writel(~0, base + INTCHW_SOFTINTCLEAR);
+}
+
+void __init bcmring_init_irq(void)
+{
+ vic_init((void __iomem *)MM_IO_BASE_INTC0, &bcmring_irq0_chip,
+ IRQ_INTC0_START, IRQ_INTC0_VALID_MASK);
+ vic_init((void __iomem *)MM_IO_BASE_INTC1, &bcmring_irq1_chip,
+ IRQ_INTC1_START, IRQ_INTC1_VALID_MASK);
+ vic_init((void __iomem *)MM_IO_BASE_SINTC, &bcmring_irq2_chip,
+ IRQ_SINTC_START, IRQ_SINTC_VALID_MASK);
+
+ /* special cases */
+ if (INTCHW_INTC1_GPIO0 & IRQ_INTC1_VALID_MASK) {
+ set_irq_handler(IRQ_GPIO0, handle_simple_irq);
+ }
+ if (INTCHW_INTC1_GPIO1 & IRQ_INTC1_VALID_MASK) {
+ set_irq_handler(IRQ_GPIO1, handle_simple_irq);
+ }
+}
diff --git a/arch/arm/mach-bcmring/mm.c b/arch/arm/mach-bcmring/mm.c
new file mode 100644
index 00000000000..0f1c37e4523
--- /dev/null
+++ b/arch/arm/mach-bcmring/mm.c
@@ -0,0 +1,56 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#include <linux/platform_device.h>
+#include <asm/mach/map.h>
+
+#include <mach/hardware.h>
+#include <mach/csp/mm_io.h>
+
+#define IO_DESC(va, sz) { .virtual = va, \
+ .pfn = __phys_to_pfn(HW_IO_VIRT_TO_PHYS(va)), \
+ .length = sz, \
+ .type = MT_DEVICE }
+
+#define MEM_DESC(va, sz) { .virtual = va, \
+ .pfn = __phys_to_pfn(HW_IO_VIRT_TO_PHYS(va)), \
+ .length = sz, \
+ .type = MT_MEMORY }
+
+static struct map_desc bcmring_io_desc[] __initdata = {
+ IO_DESC(MM_IO_BASE_NAND, SZ_64K), /* phys:0x28000000-0x28000FFF virt:0xE8000000-0xE8000FFF size:0x00010000 */
+ IO_DESC(MM_IO_BASE_UMI, SZ_64K), /* phys:0x2C000000-0x2C000FFF virt:0xEC000000-0xEC000FFF size:0x00010000 */
+
+ IO_DESC(MM_IO_BASE_BROM, SZ_64K), /* phys:0x30000000-0x3000FFFF virt:0xF3000000-0xF300FFFF size:0x00010000 */
+ MEM_DESC(MM_IO_BASE_ARAM, SZ_1M), /* phys:0x31000000-0x31FFFFFF virt:0xF3100000-0xF31FFFFF size:0x01000000 */
+ IO_DESC(MM_IO_BASE_DMA0, SZ_1M), /* phys:0x32000000-0x32FFFFFF virt:0xF3200000-0xF32FFFFF size:0x01000000 */
+ IO_DESC(MM_IO_BASE_DMA1, SZ_1M), /* phys:0x33000000-0x33FFFFFF virt:0xF3300000-0xF33FFFFF size:0x01000000 */
+ IO_DESC(MM_IO_BASE_ESW, SZ_1M), /* phys:0x34000000-0x34FFFFFF virt:0xF3400000-0xF34FFFFF size:0x01000000 */
+ IO_DESC(MM_IO_BASE_CLCD, SZ_1M), /* phys:0x35000000-0x35FFFFFF virt:0xF3500000-0xF35FFFFF size:0x01000000 */
+ IO_DESC(MM_IO_BASE_APM, SZ_1M), /* phys:0x36000000-0x36FFFFFF virt:0xF3600000-0xF36FFFFF size:0x01000000 */
+ IO_DESC(MM_IO_BASE_SPUM, SZ_1M), /* phys:0x37000000-0x37FFFFFF virt:0xF3700000-0xF37FFFFF size:0x01000000 */
+ IO_DESC(MM_IO_BASE_VPM_PROG, SZ_1M), /* phys:0x38000000-0x38FFFFFF virt:0xF3800000-0xF38FFFFF size:0x01000000 */
+ IO_DESC(MM_IO_BASE_VPM_DATA, SZ_1M), /* phys:0x3A000000-0x3AFFFFFF virt:0xF3A00000-0xF3AFFFFF size:0x01000000 */
+
+ IO_DESC(MM_IO_BASE_VRAM, SZ_64K), /* phys:0x40000000-0x4000FFFF virt:0xF4000000-0xF400FFFF size:0x00010000 */
+ IO_DESC(MM_IO_BASE_CHIPC, SZ_16M), /* phys:0x80000000-0x80FFFFFF virt:0xF8000000-0xF8FFFFFF size:0x01000000 */
+ IO_DESC(MM_IO_BASE_VPM_EXTMEM_RSVD,
+ SZ_16M), /* phys:0x0F000000-0x0FFFFFFF virt:0xF0000000-0xF0FFFFFF size:0x01000000 */
+};
+
+void __init bcmring_map_io(void)
+{
+
+ iotable_init(bcmring_io_desc, ARRAY_SIZE(bcmring_io_desc));
+}
diff --git a/arch/arm/mach-bcmring/timer.c b/arch/arm/mach-bcmring/timer.c
new file mode 100644
index 00000000000..2d415d2a8e6
--- /dev/null
+++ b/arch/arm/mach-bcmring/timer.c
@@ -0,0 +1,62 @@
+/*****************************************************************************
+* Copyright 2003 - 2008 Broadcom Corporation. All rights reserved.
+*
+* Unless you and Broadcom execute a separate written software license
+* agreement governing use of this software, this software is licensed to you
+* under the terms of the GNU General Public License version 2, available at
+* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
+*
+* Notwithstanding the above, under no circumstances may you combine this
+* software in any way with any other Broadcom software provided under a
+* license other than the GPL, without Broadcom's express prior written
+* consent.
+*****************************************************************************/
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <csp/tmrHw.h>
+
+#include <mach/timer.h>
+/* The core.c file initializes timers 1 and 3 as a linux clocksource. */
+/* The real time clock should probably be the real linux clocksource. */
+/* In the meantime, this file should agree with core.c as to the */
+/* profiling timer. If the clocksource is moved to rtc later, then */
+/* we can init the profiling timer here instead. */
+
+/* Timer 1 provides 25MHz resolution syncrhonized to scheduling and APM timing */
+/* Timer 3 provides bus freqeuncy sychronized to ACLK, but spread spectrum will */
+/* affect synchronization with scheduling and APM timing. */
+
+#define PROF_TIMER 1
+
+timer_tick_rate_t timer_get_tick_rate(void)
+{
+ return tmrHw_getCountRate(PROF_TIMER);
+}
+
+timer_tick_count_t timer_get_tick_count(void)
+{
+ return tmrHw_GetCurrentCount(PROF_TIMER); /* change downcounter to upcounter */
+}
+
+timer_msec_t timer_ticks_to_msec(timer_tick_count_t ticks)
+{
+ static int tickRateMsec;
+
+ if (tickRateMsec == 0) {
+ tickRateMsec = timer_get_tick_rate() / 1000;
+ }
+
+ return ticks / tickRateMsec;
+}
+
+timer_msec_t timer_get_msec(void)
+{
+ return timer_ticks_to_msec(timer_get_tick_count());
+}
+
+EXPORT_SYMBOL(timer_get_tick_count);
+EXPORT_SYMBOL(timer_ticks_to_msec);
+EXPORT_SYMBOL(timer_get_tick_rate);
+EXPORT_SYMBOL(timer_get_msec);
diff --git a/arch/arm/mach-ep93xx/adssphere.c b/arch/arm/mach-ep93xx/adssphere.c
index 3fbd9b0fbe2..caf6d5154ae 100644
--- a/arch/arm/mach-ep93xx/adssphere.c
+++ b/arch/arm/mach-ep93xx/adssphere.c
@@ -12,18 +12,15 @@
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/i2c.h>
+#include <linux/mtd/physmap.h>
+
#include <mach/hardware.h>
+
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
+
static struct physmap_flash_data adssphere_flash_data = {
.width = 4,
};
diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c
index 6c4c1633ed1..3dd0e2a2309 100644
--- a/arch/arm/mach-ep93xx/clock.c
+++ b/arch/arm/mach-ep93xx/clock.c
@@ -22,48 +22,39 @@
#include <mach/hardware.h>
-/*
- * The EP93xx has two external crystal oscillators. To generate the
- * required high-frequency clocks, the processor uses two phase-locked-
- * loops (PLLs) to multiply the incoming external clock signal to much
- * higher frequencies that are then divided down by programmable dividers
- * to produce the needed clocks. The PLLs operate independently of one
- * another.
- */
-#define EP93XX_EXT_CLK_RATE 14745600
-#define EP93XX_EXT_RTC_RATE 32768
-
-
struct clk {
unsigned long rate;
int users;
int sw_locked;
- u32 enable_reg;
+ void __iomem *enable_reg;
u32 enable_mask;
unsigned long (*get_rate)(struct clk *clk);
+ int (*set_rate)(struct clk *clk, unsigned long rate);
};
static unsigned long get_uart_rate(struct clk *clk);
+static int set_keytchclk_rate(struct clk *clk, unsigned long rate);
+
static struct clk clk_uart1 = {
.sw_locked = 1,
- .enable_reg = EP93XX_SYSCON_DEVICE_CONFIG,
- .enable_mask = EP93XX_SYSCON_DEVICE_CONFIG_U1EN,
+ .enable_reg = EP93XX_SYSCON_DEVCFG,
+ .enable_mask = EP93XX_SYSCON_DEVCFG_U1EN,
.get_rate = get_uart_rate,
};
static struct clk clk_uart2 = {
.sw_locked = 1,
- .enable_reg = EP93XX_SYSCON_DEVICE_CONFIG,
- .enable_mask = EP93XX_SYSCON_DEVICE_CONFIG_U2EN,
+ .enable_reg = EP93XX_SYSCON_DEVCFG,
+ .enable_mask = EP93XX_SYSCON_DEVCFG_U2EN,
.get_rate = get_uart_rate,
};
static struct clk clk_uart3 = {
.sw_locked = 1,
- .enable_reg = EP93XX_SYSCON_DEVICE_CONFIG,
- .enable_mask = EP93XX_SYSCON_DEVICE_CONFIG_U3EN,
+ .enable_reg = EP93XX_SYSCON_DEVCFG,
+ .enable_mask = EP93XX_SYSCON_DEVCFG_U3EN,
.get_rate = get_uart_rate,
};
static struct clk clk_pll1;
@@ -75,6 +66,15 @@ static struct clk clk_usb_host = {
.enable_reg = EP93XX_SYSCON_PWRCNT,
.enable_mask = EP93XX_SYSCON_PWRCNT_USH_EN,
};
+static struct clk clk_keypad = {
+ .sw_locked = 1,
+ .enable_reg = EP93XX_SYSCON_KEYTCHCLKDIV,
+ .enable_mask = EP93XX_SYSCON_KEYTCHCLKDIV_KEN,
+ .set_rate = set_keytchclk_rate,
+};
+static struct clk clk_pwm = {
+ .rate = EP93XX_EXT_CLK_RATE,
+};
/* DMA Clocks */
static struct clk clk_m2p0 = {
@@ -130,27 +130,29 @@ static struct clk clk_m2m1 = {
{ .dev_id = dev, .con_id = con, .clk = ck }
static struct clk_lookup clocks[] = {
- INIT_CK("apb:uart1", NULL, &clk_uart1),
- INIT_CK("apb:uart2", NULL, &clk_uart2),
- INIT_CK("apb:uart3", NULL, &clk_uart3),
- INIT_CK(NULL, "pll1", &clk_pll1),
- INIT_CK(NULL, "fclk", &clk_f),
- INIT_CK(NULL, "hclk", &clk_h),
- INIT_CK(NULL, "pclk", &clk_p),
- INIT_CK(NULL, "pll2", &clk_pll2),
- INIT_CK("ep93xx-ohci", NULL, &clk_usb_host),
- INIT_CK(NULL, "m2p0", &clk_m2p0),
- INIT_CK(NULL, "m2p1", &clk_m2p1),
- INIT_CK(NULL, "m2p2", &clk_m2p2),
- INIT_CK(NULL, "m2p3", &clk_m2p3),
- INIT_CK(NULL, "m2p4", &clk_m2p4),
- INIT_CK(NULL, "m2p5", &clk_m2p5),
- INIT_CK(NULL, "m2p6", &clk_m2p6),
- INIT_CK(NULL, "m2p7", &clk_m2p7),
- INIT_CK(NULL, "m2p8", &clk_m2p8),
- INIT_CK(NULL, "m2p9", &clk_m2p9),
- INIT_CK(NULL, "m2m0", &clk_m2m0),
- INIT_CK(NULL, "m2m1", &clk_m2m1),
+ INIT_CK("apb:uart1", NULL, &clk_uart1),
+ INIT_CK("apb:uart2", NULL, &clk_uart2),
+ INIT_CK("apb:uart3", NULL, &clk_uart3),
+ INIT_CK(NULL, "pll1", &clk_pll1),
+ INIT_CK(NULL, "fclk", &clk_f),
+ INIT_CK(NULL, "hclk", &clk_h),
+ INIT_CK(NULL, "pclk", &clk_p),
+ INIT_CK(NULL, "pll2", &clk_pll2),
+ INIT_CK("ep93xx-ohci", NULL, &clk_usb_host),
+ INIT_CK("ep93xx-keypad", NULL, &clk_keypad),
+ INIT_CK(NULL, "pwm_clk", &clk_pwm),
+ INIT_CK(NULL, "m2p0", &clk_m2p0),
+ INIT_CK(NULL, "m2p1", &clk_m2p1),
+ INIT_CK(NULL, "m2p2", &clk_m2p2),
+ INIT_CK(NULL, "m2p3", &clk_m2p3),
+ INIT_CK(NULL, "m2p4", &clk_m2p4),
+ INIT_CK(NULL, "m2p5", &clk_m2p5),
+ INIT_CK(NULL, "m2p6", &clk_m2p6),
+ INIT_CK(NULL, "m2p7", &clk_m2p7),
+ INIT_CK(NULL, "m2p8", &clk_m2p8),
+ INIT_CK(NULL, "m2p9", &clk_m2p9),
+ INIT_CK(NULL, "m2m0", &clk_m2m0),
+ INIT_CK(NULL, "m2m1", &clk_m2m1),
};
@@ -160,9 +162,11 @@ int clk_enable(struct clk *clk)
u32 value;
value = __raw_readl(clk->enable_reg);
+ value |= clk->enable_mask;
if (clk->sw_locked)
- __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
- __raw_writel(value | clk->enable_mask, clk->enable_reg);
+ ep93xx_syscon_swlocked_write(value, clk->enable_reg);
+ else
+ __raw_writel(value, clk->enable_reg);
}
return 0;
@@ -175,9 +179,11 @@ void clk_disable(struct clk *clk)
u32 value;
value = __raw_readl(clk->enable_reg);
+ value &= ~clk->enable_mask;
if (clk->sw_locked)
- __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
- __raw_writel(value & ~clk->enable_mask, clk->enable_reg);
+ ep93xx_syscon_swlocked_write(value, clk->enable_reg);
+ else
+ __raw_writel(value, clk->enable_reg);
}
}
EXPORT_SYMBOL(clk_disable);
@@ -202,6 +208,43 @@ unsigned long clk_get_rate(struct clk *clk)
}
EXPORT_SYMBOL(clk_get_rate);
+static int set_keytchclk_rate(struct clk *clk, unsigned long rate)
+{
+ u32 val;
+ u32 div_bit;
+
+ val = __raw_readl(clk->enable_reg);
+
+ /*
+ * The Key Matrix and ADC clocks are configured using the same
+ * System Controller register. The clock used will be either
+ * 1/4 or 1/16 the external clock rate depending on the
+ * EP93XX_SYSCON_KEYTCHCLKDIV_KDIV/EP93XX_SYSCON_KEYTCHCLKDIV_ADIV
+ * bit being set or cleared.
+ */
+ div_bit = clk->enable_mask >> 15;
+
+ if (rate == EP93XX_KEYTCHCLK_DIV4)
+ val |= div_bit;
+ else if (rate == EP93XX_KEYTCHCLK_DIV16)
+ val &= ~div_bit;
+ else
+ return -EINVAL;
+
+ ep93xx_syscon_swlocked_write(val, clk->enable_reg);
+ clk->rate = rate;
+ return 0;
+}
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ if (clk->set_rate)
+ return clk->set_rate(clk, rate);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
static char fclk_divisors[] = { 1, 2, 4, 8, 16, 1, 1, 1 };
static char hclk_divisors[] = { 1, 2, 4, 5, 6, 8, 16, 32 };
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
index 204dc5cbd0b..16b92c37ec9 100644
--- a/arch/arm/mach-ep93xx/core.c
+++ b/arch/arm/mach-ep93xx/core.c
@@ -16,40 +16,24 @@
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/sched.h>
+#include <linux/platform_device.h>
#include <linux/interrupt.h>
-#include <linux/serial.h>
-#include <linux/tty.h>
-#include <linux/bitops.h>
-#include <linux/serial_8250.h>
-#include <linux/serial_core.h>
-#include <linux/device.h>
-#include <linux/mm.h>
#include <linux/dma-mapping.h>
-#include <linux/time.h>
#include <linux/timex.h>
-#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/leds.h>
#include <linux/termios.h>
#include <linux/amba/bus.h>
#include <linux/amba/serial.h>
-#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/i2c-gpio.h>
-#include <asm/types.h>
-#include <asm/setup.h>
-#include <asm/memory.h>
#include <mach/hardware.h>
-#include <asm/irq.h>
-#include <asm/system.h>
-#include <asm/tlbflush.h>
-#include <asm/pgtable.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include <asm/mach/irq.h>
-#include <mach/gpio.h>
#include <asm/hardware/vic.h>
@@ -98,7 +82,7 @@ void __init ep93xx_map_io(void)
*/
static unsigned int last_jiffy_time;
-#define TIMER4_TICKS_PER_JIFFY ((CLOCK_TICK_RATE + (HZ/2)) / HZ)
+#define TIMER4_TICKS_PER_JIFFY DIV_ROUND_CLOSEST(CLOCK_TICK_RATE, HZ)
static irqreturn_t ep93xx_timer_interrupt(int irq, void *dev_id)
{
@@ -362,8 +346,8 @@ void __init ep93xx_init_irq(void)
{
int gpio_irq;
- vic_init((void *)EP93XX_VIC1_BASE, 0, EP93XX_VIC1_VALID_IRQ_MASK, 0);
- vic_init((void *)EP93XX_VIC2_BASE, 32, EP93XX_VIC2_VALID_IRQ_MASK, 0);
+ vic_init(EP93XX_VIC1_BASE, 0, EP93XX_VIC1_VALID_IRQ_MASK, 0);
+ vic_init(EP93XX_VIC2_BASE, 32, EP93XX_VIC2_VALID_IRQ_MASK, 0);
for (gpio_irq = gpio_to_irq(0);
gpio_irq <= gpio_to_irq(EP93XX_GPIO_LINE_MAX_IRQ); ++gpio_irq) {
@@ -385,6 +369,47 @@ void __init ep93xx_init_irq(void)
/*************************************************************************
+ * EP93xx System Controller Software Locked register handling
+ *************************************************************************/
+
+/*
+ * syscon_swlock prevents anything else from writing to the syscon
+ * block while a software locked register is being written.
+ */
+static DEFINE_SPINLOCK(syscon_swlock);
+
+void ep93xx_syscon_swlocked_write(unsigned int val, void __iomem *reg)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&syscon_swlock, flags);
+
+ __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
+ __raw_writel(val, reg);
+
+ spin_unlock_irqrestore(&syscon_swlock, flags);
+}
+EXPORT_SYMBOL(ep93xx_syscon_swlocked_write);
+
+void ep93xx_devcfg_set_clear(unsigned int set_bits, unsigned int clear_bits)
+{
+ unsigned long flags;
+ unsigned int val;
+
+ spin_lock_irqsave(&syscon_swlock, flags);
+
+ val = __raw_readl(EP93XX_SYSCON_DEVCFG);
+ val |= set_bits;
+ val &= ~clear_bits;
+ __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
+ __raw_writel(val, EP93XX_SYSCON_DEVCFG);
+
+ spin_unlock_irqrestore(&syscon_swlock, flags);
+}
+EXPORT_SYMBOL(ep93xx_devcfg_set_clear);
+
+
+/*************************************************************************
* EP93xx peripheral handling
*************************************************************************/
#define EP93XX_UART_MCR_OFFSET (0x0100)
@@ -517,10 +542,8 @@ static struct platform_device ep93xx_eth_device = {
void __init ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr)
{
- if (copy_addr) {
- memcpy(data->dev_addr,
- (void *)(EP93XX_ETHERNET_BASE + 0x50), 6);
- }
+ if (copy_addr)
+ memcpy_fromio(data->dev_addr, EP93XX_ETHERNET_BASE + 0x50, 6);
ep93xx_eth_data = *data;
platform_device_register(&ep93xx_eth_device);
@@ -546,19 +569,125 @@ void __init ep93xx_register_i2c(struct i2c_board_info *devices, int num)
platform_device_register(&ep93xx_i2c_device);
}
+
+/*************************************************************************
+ * EP93xx LEDs
+ *************************************************************************/
+static struct gpio_led ep93xx_led_pins[] = {
+ {
+ .name = "platform:grled",
+ .gpio = EP93XX_GPIO_LINE_GRLED,
+ }, {
+ .name = "platform:rdled",
+ .gpio = EP93XX_GPIO_LINE_RDLED,
+ },
+};
+
+static struct gpio_led_platform_data ep93xx_led_data = {
+ .num_leds = ARRAY_SIZE(ep93xx_led_pins),
+ .leds = ep93xx_led_pins,
+};
+
+static struct platform_device ep93xx_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &ep93xx_led_data,
+ },
+};
+
+
+/*************************************************************************
+ * EP93xx pwm peripheral handling
+ *************************************************************************/
+static struct resource ep93xx_pwm0_resource[] = {
+ {
+ .start = EP93XX_PWM_PHYS_BASE,
+ .end = EP93XX_PWM_PHYS_BASE + 0x10 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device ep93xx_pwm0_device = {
+ .name = "ep93xx-pwm",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(ep93xx_pwm0_resource),
+ .resource = ep93xx_pwm0_resource,
+};
+
+static struct resource ep93xx_pwm1_resource[] = {
+ {
+ .start = EP93XX_PWM_PHYS_BASE + 0x20,
+ .end = EP93XX_PWM_PHYS_BASE + 0x30 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device ep93xx_pwm1_device = {
+ .name = "ep93xx-pwm",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(ep93xx_pwm1_resource),
+ .resource = ep93xx_pwm1_resource,
+};
+
+void __init ep93xx_register_pwm(int pwm0, int pwm1)
+{
+ if (pwm0)
+ platform_device_register(&ep93xx_pwm0_device);
+
+ /* NOTE: EP9307 does not have PWMOUT1 (pin EGPIO14) */
+ if (pwm1)
+ platform_device_register(&ep93xx_pwm1_device);
+}
+
+int ep93xx_pwm_acquire_gpio(struct platform_device *pdev)
+{
+ int err;
+
+ if (pdev->id == 0) {
+ err = 0;
+ } else if (pdev->id == 1) {
+ err = gpio_request(EP93XX_GPIO_LINE_EGPIO14,
+ dev_name(&pdev->dev));
+ if (err)
+ return err;
+ err = gpio_direction_output(EP93XX_GPIO_LINE_EGPIO14, 0);
+ if (err)
+ goto fail;
+
+ /* PWM 1 output on EGPIO[14] */
+ ep93xx_devcfg_set_bits(EP93XX_SYSCON_DEVCFG_PONG);
+ } else {
+ err = -ENODEV;
+ }
+
+ return err;
+
+fail:
+ gpio_free(EP93XX_GPIO_LINE_EGPIO14);
+ return err;
+}
+EXPORT_SYMBOL(ep93xx_pwm_acquire_gpio);
+
+void ep93xx_pwm_release_gpio(struct platform_device *pdev)
+{
+ if (pdev->id == 1) {
+ gpio_direction_input(EP93XX_GPIO_LINE_EGPIO14);
+ gpio_free(EP93XX_GPIO_LINE_EGPIO14);
+
+ /* EGPIO[14] used for GPIO */
+ ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_PONG);
+ }
+}
+EXPORT_SYMBOL(ep93xx_pwm_release_gpio);
+
+
extern void ep93xx_gpio_init(void);
void __init ep93xx_init_devices(void)
{
- unsigned int v;
-
- /*
- * Disallow access to MaverickCrunch initially.
- */
- v = __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG);
- v &= ~EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE;
- __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
- __raw_writel(v, EP93XX_SYSCON_DEVICE_CONFIG);
+ /* Disallow access to MaverickCrunch initially */
+ ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_CPENA);
ep93xx_gpio_init();
@@ -568,4 +697,5 @@ void __init ep93xx_init_devices(void)
platform_device_register(&ep93xx_rtc_device);
platform_device_register(&ep93xx_ohci_device);
+ platform_device_register(&ep93xx_leds);
}
diff --git a/arch/arm/mach-ep93xx/edb93xx.c b/arch/arm/mach-ep93xx/edb93xx.c
index e9e45b92457..73145ae5d3f 100644
--- a/arch/arm/mach-ep93xx/edb93xx.c
+++ b/arch/arm/mach-ep93xx/edb93xx.c
@@ -26,18 +26,16 @@
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
-#include <linux/io.h>
#include <linux/i2c.h>
+#include <linux/mtd/physmap.h>
+
#include <mach/hardware.h>
+
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
+
static struct physmap_flash_data edb93xx_flash_data;
static struct resource edb93xx_flash_resource = {
diff --git a/arch/arm/mach-ep93xx/gesbc9312.c b/arch/arm/mach-ep93xx/gesbc9312.c
index 3bad500b71b..3da7ca816d1 100644
--- a/arch/arm/mach-ep93xx/gesbc9312.c
+++ b/arch/arm/mach-ep93xx/gesbc9312.c
@@ -12,18 +12,15 @@
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/i2c.h>
+#include <linux/mtd/physmap.h>
+
#include <mach/hardware.h>
+
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
+
static struct physmap_flash_data gesbc9312_flash_data = {
.width = 4,
};
diff --git a/arch/arm/mach-ep93xx/gpio.c b/arch/arm/mach-ep93xx/gpio.c
index 482cf3d2fbc..1ea8871e03a 100644
--- a/arch/arm/mach-ep93xx/gpio.c
+++ b/arch/arm/mach-ep93xx/gpio.c
@@ -17,15 +17,16 @@
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
-#include <mach/ep93xx-regs.h>
-#include <asm/gpio.h>
+#include <mach/hardware.h>
struct ep93xx_gpio_chip {
struct gpio_chip chip;
- unsigned int data_reg;
- unsigned int data_dir_reg;
+ void __iomem *data_reg;
+ void __iomem *data_dir_reg;
};
#define to_ep93xx_gpio_chip(c) container_of(c, struct ep93xx_gpio_chip, chip)
@@ -111,15 +112,61 @@ static void ep93xx_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
struct ep93xx_gpio_chip *ep93xx_chip = to_ep93xx_gpio_chip(chip);
u8 data_reg, data_dir_reg;
- int i;
+ int gpio, i;
data_reg = __raw_readb(ep93xx_chip->data_reg);
data_dir_reg = __raw_readb(ep93xx_chip->data_dir_reg);
- for (i = 0; i < chip->ngpio; i++)
- seq_printf(s, "GPIO %s%d: %s %s\n", chip->label, i,
- (data_reg & (1 << i)) ? "set" : "clear",
- (data_dir_reg & (1 << i)) ? "out" : "in");
+ gpio = ep93xx_chip->chip.base;
+ for (i = 0; i < chip->ngpio; i++, gpio++) {
+ int is_out = data_dir_reg & (1 << i);
+
+ seq_printf(s, " %s%d gpio-%-3d (%-12s) %s %s",
+ chip->label, i, gpio,
+ gpiochip_is_requested(chip, i) ? : "",
+ is_out ? "out" : "in ",
+ (data_reg & (1 << i)) ? "hi" : "lo");
+
+ if (!is_out) {
+ int irq = gpio_to_irq(gpio);
+ struct irq_desc *desc = irq_desc + irq;
+
+ if (irq >= 0 && desc->action) {
+ char *trigger;
+
+ switch (desc->status & IRQ_TYPE_SENSE_MASK) {
+ case IRQ_TYPE_NONE:
+ trigger = "(default)";
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ trigger = "edge-falling";
+ break;
+ case IRQ_TYPE_EDGE_RISING:
+ trigger = "edge-rising";
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ trigger = "edge-both";
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ trigger = "level-high";
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ trigger = "level-low";
+ break;
+ default:
+ trigger = "?trigger?";
+ break;
+ }
+
+ seq_printf(s, " irq-%d %s%s",
+ irq, trigger,
+ (desc->status & IRQ_WAKEUP)
+ ? " wakeup" : "");
+ }
+ }
+
+ seq_printf(s, "\n");
+ }
}
#define EP93XX_GPIO_BANK(name, dr, ddr, base_gpio) \
diff --git a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
index 967c079180d..ea78e908fc8 100644
--- a/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
+++ b/arch/arm/mach-ep93xx/include/mach/ep93xx-regs.h
@@ -52,40 +52,43 @@
#define EP93XX_AHB_VIRT_BASE 0xfef00000
#define EP93XX_AHB_SIZE 0x00100000
+#define EP93XX_AHB_IOMEM(x) IOMEM(EP93XX_AHB_VIRT_BASE + (x))
+
#define EP93XX_APB_PHYS_BASE 0x80800000
#define EP93XX_APB_VIRT_BASE 0xfed00000
#define EP93XX_APB_SIZE 0x00200000
+#define EP93XX_APB_IOMEM(x) IOMEM(EP93XX_APB_VIRT_BASE + (x))
+
/* AHB peripherals */
-#define EP93XX_DMA_BASE ((void __iomem *) \
- (EP93XX_AHB_VIRT_BASE + 0x00000000))
+#define EP93XX_DMA_BASE EP93XX_AHB_IOMEM(0x00000000)
-#define EP93XX_ETHERNET_BASE (EP93XX_AHB_VIRT_BASE + 0x00010000)
#define EP93XX_ETHERNET_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x00010000)
+#define EP93XX_ETHERNET_BASE EP93XX_AHB_IOMEM(0x00010000)
-#define EP93XX_USB_BASE (EP93XX_AHB_VIRT_BASE + 0x00020000)
#define EP93XX_USB_PHYS_BASE (EP93XX_AHB_PHYS_BASE + 0x00020000)
+#define EP93XX_USB_BASE EP93XX_AHB_IOMEM(0x00020000)
-#define EP93XX_RASTER_BASE (EP93XX_AHB_VIRT_BASE + 0x00030000)
+#define EP93XX_RASTER_BASE EP93XX_AHB_IOMEM(0x00030000)
-#define EP93XX_GRAPHICS_ACCEL_BASE (EP93XX_AHB_VIRT_BASE + 0x00040000)
+#define EP93XX_GRAPHICS_ACCEL_BASE EP93XX_AHB_IOMEM(0x00040000)
-#define EP93XX_SDRAM_CONTROLLER_BASE (EP93XX_AHB_VIRT_BASE + 0x00060000)
+#define EP93XX_SDRAM_CONTROLLER_BASE EP93XX_AHB_IOMEM(0x00060000)
-#define EP93XX_PCMCIA_CONTROLLER_BASE (EP93XX_AHB_VIRT_BASE + 0x00080000)
+#define EP93XX_PCMCIA_CONTROLLER_BASE EP93XX_AHB_IOMEM(0x00080000)
-#define EP93XX_BOOT_ROM_BASE (EP93XX_AHB_VIRT_BASE + 0x00090000)
+#define EP93XX_BOOT_ROM_BASE EP93XX_AHB_IOMEM(0x00090000)
-#define EP93XX_IDE_BASE (EP93XX_AHB_VIRT_BASE + 0x000a0000)
+#define EP93XX_IDE_BASE EP93XX_AHB_IOMEM(0x000a0000)
-#define EP93XX_VIC1_BASE (EP93XX_AHB_VIRT_BASE + 0x000b0000)
+#define EP93XX_VIC1_BASE EP93XX_AHB_IOMEM(0x000b0000)
-#define EP93XX_VIC2_BASE (EP93XX_AHB_VIRT_BASE + 0x000c0000)
+#define EP93XX_VIC2_BASE EP93XX_AHB_IOMEM(0x000c0000)
/* APB peripherals */
-#define EP93XX_TIMER_BASE (EP93XX_APB_VIRT_BASE + 0x00010000)
+#define EP93XX_TIMER_BASE EP93XX_APB_IOMEM(0x00010000)
#define EP93XX_TIMER_REG(x) (EP93XX_TIMER_BASE + (x))
#define EP93XX_TIMER1_LOAD EP93XX_TIMER_REG(0x00)
#define EP93XX_TIMER1_VALUE EP93XX_TIMER_REG(0x04)
@@ -102,11 +105,11 @@
#define EP93XX_TIMER3_CONTROL EP93XX_TIMER_REG(0x88)
#define EP93XX_TIMER3_CLEAR EP93XX_TIMER_REG(0x8c)
-#define EP93XX_I2S_BASE (EP93XX_APB_VIRT_BASE + 0x00020000)
+#define EP93XX_I2S_BASE EP93XX_APB_IOMEM(0x00020000)
-#define EP93XX_SECURITY_BASE (EP93XX_APB_VIRT_BASE + 0x00030000)
+#define EP93XX_SECURITY_BASE EP93XX_APB_IOMEM(0x00030000)
-#define EP93XX_GPIO_BASE (EP93XX_APB_VIRT_BASE + 0x00040000)
+#define EP93XX_GPIO_BASE EP93XX_APB_IOMEM(0x00040000)
#define EP93XX_GPIO_REG(x) (EP93XX_GPIO_BASE + (x))
#define EP93XX_GPIO_F_INT_TYPE1 EP93XX_GPIO_REG(0x4c)
#define EP93XX_GPIO_F_INT_TYPE2 EP93XX_GPIO_REG(0x50)
@@ -124,32 +127,33 @@
#define EP93XX_GPIO_B_INT_ENABLE EP93XX_GPIO_REG(0xb8)
#define EP93XX_GPIO_B_INT_STATUS EP93XX_GPIO_REG(0xbc)
-#define EP93XX_AAC_BASE (EP93XX_APB_VIRT_BASE + 0x00080000)
+#define EP93XX_AAC_BASE EP93XX_APB_IOMEM(0x00080000)
-#define EP93XX_SPI_BASE (EP93XX_APB_VIRT_BASE + 0x000a0000)
+#define EP93XX_SPI_BASE EP93XX_APB_IOMEM(0x000a0000)
-#define EP93XX_IRDA_BASE (EP93XX_APB_VIRT_BASE + 0x000b0000)
+#define EP93XX_IRDA_BASE EP93XX_APB_IOMEM(0x000b0000)
-#define EP93XX_UART1_BASE (EP93XX_APB_VIRT_BASE + 0x000c0000)
#define EP93XX_UART1_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x000c0000)
+#define EP93XX_UART1_BASE EP93XX_APB_IOMEM(0x000c0000)
-#define EP93XX_UART2_BASE (EP93XX_APB_VIRT_BASE + 0x000d0000)
#define EP93XX_UART2_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x000d0000)
+#define EP93XX_UART2_BASE EP93XX_APB_IOMEM(0x000d0000)
-#define EP93XX_UART3_BASE (EP93XX_APB_VIRT_BASE + 0x000e0000)
#define EP93XX_UART3_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x000e0000)
+#define EP93XX_UART3_BASE EP93XX_APB_IOMEM(0x000e0000)
-#define EP93XX_KEY_MATRIX_BASE (EP93XX_APB_VIRT_BASE + 0x000f0000)
+#define EP93XX_KEY_MATRIX_BASE EP93XX_APB_IOMEM(0x000f0000)
-#define EP93XX_ADC_BASE (EP93XX_APB_VIRT_BASE + 0x00100000)
-#define EP93XX_TOUCHSCREEN_BASE (EP93XX_APB_VIRT_BASE + 0x00100000)
+#define EP93XX_ADC_BASE EP93XX_APB_IOMEM(0x00100000)
+#define EP93XX_TOUCHSCREEN_BASE EP93XX_APB_IOMEM(0x00100000)
-#define EP93XX_PWM_BASE (EP93XX_APB_VIRT_BASE + 0x00110000)
+#define EP93XX_PWM_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x00110000)
+#define EP93XX_PWM_BASE EP93XX_APB_IOMEM(0x00110000)
-#define EP93XX_RTC_BASE (EP93XX_APB_VIRT_BASE + 0x00120000)
#define EP93XX_RTC_PHYS_BASE (EP93XX_APB_PHYS_BASE + 0x00120000)
+#define EP93XX_RTC_BASE EP93XX_APB_IOMEM(0x00120000)
-#define EP93XX_SYSCON_BASE (EP93XX_APB_VIRT_BASE + 0x00130000)
+#define EP93XX_SYSCON_BASE EP93XX_APB_IOMEM(0x00130000)
#define EP93XX_SYSCON_REG(x) (EP93XX_SYSCON_BASE + (x))
#define EP93XX_SYSCON_POWER_STATE EP93XX_SYSCON_REG(0x00)
#define EP93XX_SYSCON_PWRCNT EP93XX_SYSCON_REG(0x04)
@@ -172,14 +176,45 @@
#define EP93XX_SYSCON_STANDBY EP93XX_SYSCON_REG(0x0c)
#define EP93XX_SYSCON_CLOCK_SET1 EP93XX_SYSCON_REG(0x20)
#define EP93XX_SYSCON_CLOCK_SET2 EP93XX_SYSCON_REG(0x24)
-#define EP93XX_SYSCON_DEVICE_CONFIG EP93XX_SYSCON_REG(0x80)
-#define EP93XX_SYSCON_DEVICE_CONFIG_U3EN (1<<24)
-#define EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE (1<<23)
-#define EP93XX_SYSCON_DEVICE_CONFIG_U2EN (1<<20)
-#define EP93XX_SYSCON_DEVICE_CONFIG_U1EN (1<<18)
+#define EP93XX_SYSCON_DEVCFG EP93XX_SYSCON_REG(0x80)
+#define EP93XX_SYSCON_DEVCFG_SWRST (1<<31)
+#define EP93XX_SYSCON_DEVCFG_D1ONG (1<<30)
+#define EP93XX_SYSCON_DEVCFG_D0ONG (1<<29)
+#define EP93XX_SYSCON_DEVCFG_IONU2 (1<<28)
+#define EP93XX_SYSCON_DEVCFG_GONK (1<<27)
+#define EP93XX_SYSCON_DEVCFG_TONG (1<<26)
+#define EP93XX_SYSCON_DEVCFG_MONG (1<<25)
+#define EP93XX_SYSCON_DEVCFG_U3EN (1<<24)
+#define EP93XX_SYSCON_DEVCFG_CPENA (1<<23)
+#define EP93XX_SYSCON_DEVCFG_A2ONG (1<<22)
+#define EP93XX_SYSCON_DEVCFG_A1ONG (1<<21)
+#define EP93XX_SYSCON_DEVCFG_U2EN (1<<20)
+#define EP93XX_SYSCON_DEVCFG_EXVC (1<<19)
+#define EP93XX_SYSCON_DEVCFG_U1EN (1<<18)
+#define EP93XX_SYSCON_DEVCFG_TIN (1<<17)
+#define EP93XX_SYSCON_DEVCFG_HC3IN (1<<15)
+#define EP93XX_SYSCON_DEVCFG_HC3EN (1<<14)
+#define EP93XX_SYSCON_DEVCFG_HC1IN (1<<13)
+#define EP93XX_SYSCON_DEVCFG_HC1EN (1<<12)
+#define EP93XX_SYSCON_DEVCFG_HONIDE (1<<11)
+#define EP93XX_SYSCON_DEVCFG_GONIDE (1<<10)
+#define EP93XX_SYSCON_DEVCFG_PONG (1<<9)
+#define EP93XX_SYSCON_DEVCFG_EONIDE (1<<8)
+#define EP93XX_SYSCON_DEVCFG_I2SONSSP (1<<7)
+#define EP93XX_SYSCON_DEVCFG_I2SONAC97 (1<<6)
+#define EP93XX_SYSCON_DEVCFG_RASONP3 (1<<4)
+#define EP93XX_SYSCON_DEVCFG_RAS (1<<3)
+#define EP93XX_SYSCON_DEVCFG_ADCPD (1<<2)
+#define EP93XX_SYSCON_DEVCFG_KEYS (1<<1)
+#define EP93XX_SYSCON_DEVCFG_SHENA (1<<0)
+#define EP93XX_SYSCON_KEYTCHCLKDIV EP93XX_SYSCON_REG(0x90)
+#define EP93XX_SYSCON_KEYTCHCLKDIV_TSEN (1<<31)
+#define EP93XX_SYSCON_KEYTCHCLKDIV_ADIV (1<<16)
+#define EP93XX_SYSCON_KEYTCHCLKDIV_KEN (1<<15)
+#define EP93XX_SYSCON_KEYTCHCLKDIV_KDIV (1<<0)
#define EP93XX_SYSCON_SWLOCK EP93XX_SYSCON_REG(0xc0)
-#define EP93XX_WATCHDOG_BASE (EP93XX_APB_VIRT_BASE + 0x00140000)
+#define EP93XX_WATCHDOG_BASE EP93XX_APB_IOMEM(0x00140000)
#endif
diff --git a/arch/arm/mach-ep93xx/include/mach/hardware.h b/arch/arm/mach-ep93xx/include/mach/hardware.h
index 2866297310b..349fa7cb72d 100644
--- a/arch/arm/mach-ep93xx/include/mach/hardware.h
+++ b/arch/arm/mach-ep93xx/include/mach/hardware.h
@@ -4,12 +4,23 @@
#ifndef __ASM_ARCH_HARDWARE_H
#define __ASM_ARCH_HARDWARE_H
-#include "ep93xx-regs.h"
+#include <mach/ep93xx-regs.h>
+#include <mach/platform.h>
#define pcibios_assign_all_busses() 0
-#include "platform.h"
+/*
+ * The EP93xx has two external crystal oscillators. To generate the
+ * required high-frequency clocks, the processor uses two phase-locked-
+ * loops (PLLs) to multiply the incoming external clock signal to much
+ * higher frequencies that are then divided down by programmable dividers
+ * to produce the needed clocks. The PLLs operate independently of one
+ * another.
+ */
+#define EP93XX_EXT_CLK_RATE 14745600
+#define EP93XX_EXT_RTC_RATE 32768
-#include "ts72xx.h"
+#define EP93XX_KEYTCHCLK_DIV4 (EP93XX_EXT_CLK_RATE / 4)
+#define EP93XX_KEYTCHCLK_DIV16 (EP93XX_EXT_CLK_RATE / 16)
#endif
diff --git a/arch/arm/mach-ep93xx/include/mach/io.h b/arch/arm/mach-ep93xx/include/mach/io.h
index fd5f081cc8b..cebcc1c53d6 100644
--- a/arch/arm/mach-ep93xx/include/mach/io.h
+++ b/arch/arm/mach-ep93xx/include/mach/io.h
@@ -1,8 +1,21 @@
/*
* arch/arm/mach-ep93xx/include/mach/io.h
*/
+#ifndef __ASM_MACH_IO_H
+#define __ASM_MACH_IO_H
#define IO_SPACE_LIMIT 0xffffffff
-#define __io(p) __typesafe_io(p)
-#define __mem_pci(p) (p)
+#define __io(p) __typesafe_io(p)
+#define __mem_pci(p) (p)
+
+/*
+ * A typesafe __io() variation for variable initialisers
+ */
+#ifdef __ASSEMBLER__
+#define IOMEM(p) p
+#else
+#define IOMEM(p) ((void __iomem __force *)(p))
+#endif
+
+#endif /* __ASM_MACH_IO_H */
diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h
index 05f0f4f2f3c..5f5fa6574d3 100644
--- a/arch/arm/mach-ep93xx/include/mach/platform.h
+++ b/arch/arm/mach-ep93xx/include/mach/platform.h
@@ -5,6 +5,7 @@
#ifndef __ASSEMBLY__
struct i2c_board_info;
+struct platform_device;
struct ep93xx_eth_data
{
@@ -15,8 +16,27 @@ struct ep93xx_eth_data
void ep93xx_map_io(void);
void ep93xx_init_irq(void);
void ep93xx_init_time(unsigned long);
+
+/* EP93xx System Controller software locked register write */
+void ep93xx_syscon_swlocked_write(unsigned int val, void __iomem *reg);
+void ep93xx_devcfg_set_clear(unsigned int set_bits, unsigned int clear_bits);
+
+static inline void ep93xx_devcfg_set_bits(unsigned int bits)
+{
+ ep93xx_devcfg_set_clear(bits, 0x00);
+}
+
+static inline void ep93xx_devcfg_clear_bits(unsigned int bits)
+{
+ ep93xx_devcfg_set_clear(0x00, bits);
+}
+
void ep93xx_register_eth(struct ep93xx_eth_data *data, int copy_addr);
void ep93xx_register_i2c(struct i2c_board_info *devices, int num);
+void ep93xx_register_pwm(int pwm0, int pwm1);
+int ep93xx_pwm_acquire_gpio(struct platform_device *pdev);
+void ep93xx_pwm_release_gpio(struct platform_device *pdev);
+
void ep93xx_init_devices(void);
extern struct sys_timer ep93xx_timer;
diff --git a/arch/arm/mach-ep93xx/include/mach/system.h b/arch/arm/mach-ep93xx/include/mach/system.h
index ed8f35e4f06..6d661fe9d66 100644
--- a/arch/arm/mach-ep93xx/include/mach/system.h
+++ b/arch/arm/mach-ep93xx/include/mach/system.h
@@ -11,15 +11,13 @@ static inline void arch_idle(void)
static inline void arch_reset(char mode, const char *cmd)
{
- u32 devicecfg;
-
local_irq_disable();
- devicecfg = __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG);
- __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
- __raw_writel(devicecfg | 0x80000000, EP93XX_SYSCON_DEVICE_CONFIG);
- __raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
- __raw_writel(devicecfg & ~0x80000000, EP93XX_SYSCON_DEVICE_CONFIG);
+ /*
+ * Set then clear the SWRST bit to initiate a software reset
+ */
+ ep93xx_devcfg_set_bits(EP93XX_SYSCON_DEVCFG_SWRST);
+ ep93xx_devcfg_clear_bits(EP93XX_SYSCON_DEVCFG_SWRST);
while (1)
;
diff --git a/arch/arm/mach-ep93xx/include/mach/ts72xx.h b/arch/arm/mach-ep93xx/include/mach/ts72xx.h
index 411734422c1..3bd934e9a7f 100644
--- a/arch/arm/mach-ep93xx/include/mach/ts72xx.h
+++ b/arch/arm/mach-ep93xx/include/mach/ts72xx.h
@@ -67,7 +67,6 @@
#ifndef __ASSEMBLY__
-#include <linux/io.h>
static inline int board_is_ts7200(void)
{
diff --git a/arch/arm/mach-ep93xx/micro9.c b/arch/arm/mach-ep93xx/micro9.c
index 15d6815d78c..0a313e82fb7 100644
--- a/arch/arm/mach-ep93xx/micro9.c
+++ b/arch/arm/mach-ep93xx/micro9.c
@@ -9,21 +9,16 @@
* published by the Free Software Foundation.
*/
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
#include <linux/kernel.h>
-#include <linux/mm.h>
+#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/sched.h>
-#include <linux/io.h>
-#include <linux/i2c.h>
#include <linux/mtd/physmap.h>
#include <mach/hardware.h>
-#include <asm/mach/arch.h>
#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+
static struct ep93xx_eth_data micro9_eth_data = {
.phy_id = 0x1f,
diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c
index aaf1371412a..259f7822ba5 100644
--- a/arch/arm/mach-ep93xx/ts72xx.c
+++ b/arch/arm/mach-ep93xx/ts72xx.c
@@ -12,19 +12,18 @@
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/ioport.h>
-#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
-#include <linux/m48t86.h>
#include <linux/io.h>
-#include <linux/i2c.h>
+#include <linux/m48t86.h>
+#include <linux/mtd/physmap.h>
+
#include <mach/hardware.h>
+#include <mach/ts72xx.h>
+
#include <asm/mach-types.h>
-#include <asm/mach/arch.h>
#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+
static struct map_desc ts72xx_io_desc[] __initdata = {
{
diff --git a/arch/arm/mach-integrator/include/mach/hardware.h b/arch/arm/mach-integrator/include/mach/hardware.h
index 1251319ef9a..d795642fad2 100644
--- a/arch/arm/mach-integrator/include/mach/hardware.h
+++ b/arch/arm/mach-integrator/include/mach/hardware.h
@@ -36,8 +36,12 @@
#define PCIO_BASE PCI_IO_VADDR
#define PCIMEM_BASE PCI_MEMORY_VADDR
+#ifdef CONFIG_MMU
/* macro to get at IO space when running virtually */
#define IO_ADDRESS(x) (((x) >> 4) + IO_BASE)
+#else
+#define IO_ADDRESS(x) (x)
+#endif
#define pcibios_assign_all_busses() 1
diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c
index 4ac04055c2e..2a318eba1b0 100644
--- a/arch/arm/mach-integrator/integrator_cp.c
+++ b/arch/arm/mach-integrator/integrator_cp.c
@@ -49,14 +49,14 @@
#define INTCP_PA_CLCD_BASE 0xc0000000
-#define INTCP_VA_CIC_BASE 0xf1000040
-#define INTCP_VA_PIC_BASE 0xf1400000
-#define INTCP_VA_SIC_BASE 0xfca00000
+#define INTCP_VA_CIC_BASE IO_ADDRESS(INTEGRATOR_HDR_BASE) + 0x40
+#define INTCP_VA_PIC_BASE IO_ADDRESS(INTEGRATOR_IC_BASE)
+#define INTCP_VA_SIC_BASE IO_ADDRESS(0xca000000)
#define INTCP_PA_ETH_BASE 0xc8000000
#define INTCP_ETH_SIZE 0x10
-#define INTCP_VA_CTRL_BASE 0xfcb00000
+#define INTCP_VA_CTRL_BASE IO_ADDRESS(0xcb000000)
#define INTCP_FLASHPROG 0x04
#define CINTEGRATOR_FLASHPROG_FLVPPEN (1 << 0)
#define CINTEGRATOR_FLASHPROG_FLWREN (1 << 1)
@@ -121,12 +121,12 @@ static struct map_desc intcp_io_desc[] __initdata = {
.length = SZ_4K,
.type = MT_DEVICE
}, {
- .virtual = 0xfca00000,
+ .virtual = IO_ADDRESS(0xca000000),
.pfn = __phys_to_pfn(0xca000000),
.length = SZ_4K,
.type = MT_DEVICE
}, {
- .virtual = 0xfcb00000,
+ .virtual = IO_ADDRESS(0xcb000000),
.pfn = __phys_to_pfn(0xcb000000),
.length = SZ_4K,
.type = MT_DEVICE
@@ -394,8 +394,8 @@ static struct platform_device *intcp_devs[] __initdata = {
*/
static unsigned int mmc_status(struct device *dev)
{
- unsigned int status = readl(0xfca00004);
- writel(8, 0xfcb00008);
+ unsigned int status = readl(IO_ADDRESS(0xca000000) + 4);
+ writel(8, IO_ADDRESS(0xcb000000) + 8);
return status & 8;
}
@@ -403,6 +403,8 @@ static unsigned int mmc_status(struct device *dev)
static struct mmc_platform_data mmc_data = {
.ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
.status = mmc_status,
+ .gpio_wp = -1,
+ .gpio_cd = -1,
};
static struct amba_device mmc_device = {
diff --git a/arch/arm/mach-kirkwood/Kconfig b/arch/arm/mach-kirkwood/Kconfig
index 25100f7acf4..0aca451b216 100644
--- a/arch/arm/mach-kirkwood/Kconfig
+++ b/arch/arm/mach-kirkwood/Kconfig
@@ -38,6 +38,12 @@ config MACH_TS219
Say 'Y' here if you want your kernel to support the
QNAP TS-119 and TS-219 Turbo NAS devices.
+config MACH_OPENRD_BASE
+ bool "Marvell OpenRD Base Board"
+ help
+ Say 'Y' here if you want your kernel to support the
+ Marvell OpenRD Base Board.
+
endmenu
endif
diff --git a/arch/arm/mach-kirkwood/Makefile b/arch/arm/mach-kirkwood/Makefile
index 9dd680e964d..80ab0ec90ee 100644
--- a/arch/arm/mach-kirkwood/Makefile
+++ b/arch/arm/mach-kirkwood/Makefile
@@ -6,5 +6,6 @@ obj-$(CONFIG_MACH_RD88F6281) += rd88f6281-setup.o
obj-$(CONFIG_MACH_MV88F6281GTW_GE) += mv88f6281gtw_ge-setup.o
obj-$(CONFIG_MACH_SHEEVAPLUG) += sheevaplug-setup.o
obj-$(CONFIG_MACH_TS219) += ts219-setup.o
+obj-$(CONFIG_MACH_OPENRD_BASE) += openrd_base-setup.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
diff --git a/arch/arm/mach-kirkwood/common.c b/arch/arm/mach-kirkwood/common.c
index 0f691983801..0acb61f3c10 100644
--- a/arch/arm/mach-kirkwood/common.c
+++ b/arch/arm/mach-kirkwood/common.c
@@ -838,7 +838,8 @@ int __init kirkwood_find_tclk(void)
u32 dev, rev;
kirkwood_pcie_id(&dev, &rev);
- if (dev == MV88F6281_DEV_ID && rev == MV88F6281_REV_A0)
+ if (dev == MV88F6281_DEV_ID && (rev == MV88F6281_REV_A0 ||
+ rev == MV88F6281_REV_A1))
return 200000000;
return 166666667;
@@ -872,6 +873,8 @@ static char * __init kirkwood_id(void)
return "MV88F6281-Z0";
else if (rev == MV88F6281_REV_A0)
return "MV88F6281-A0";
+ else if (rev == MV88F6281_REV_A1)
+ return "MV88F6281-A1";
else
return "MV88F6281-Rev-Unsupported";
} else if (dev == MV88F6192_DEV_ID) {
diff --git a/arch/arm/mach-kirkwood/include/mach/kirkwood.h b/arch/arm/mach-kirkwood/include/mach/kirkwood.h
index 07af858814a..54c132731d2 100644
--- a/arch/arm/mach-kirkwood/include/mach/kirkwood.h
+++ b/arch/arm/mach-kirkwood/include/mach/kirkwood.h
@@ -101,6 +101,7 @@
#define MV88F6281_DEV_ID 0x6281
#define MV88F6281_REV_Z0 0
#define MV88F6281_REV_A0 2
+#define MV88F6281_REV_A1 3
#define MV88F6192_DEV_ID 0x6192
#define MV88F6192_REV_Z0 0
diff --git a/arch/arm/mach-kirkwood/openrd_base-setup.c b/arch/arm/mach-kirkwood/openrd_base-setup.c
new file mode 100644
index 00000000000..947dfb8cd5b
--- /dev/null
+++ b/arch/arm/mach-kirkwood/openrd_base-setup.c
@@ -0,0 +1,84 @@
+/*
+ * arch/arm/mach-kirkwood/openrd_base-setup.c
+ *
+ * Marvell OpenRD Base Board Setup
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/partitions.h>
+#include <linux/ata_platform.h>
+#include <linux/mv643xx_eth.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/kirkwood.h>
+#include <plat/mvsdio.h>
+#include "common.h"
+#include "mpp.h"
+
+static struct mtd_partition openrd_base_nand_parts[] = {
+ {
+ .name = "u-boot",
+ .offset = 0,
+ .size = SZ_1M
+ }, {
+ .name = "uImage",
+ .offset = MTDPART_OFS_NXTBLK,
+ .size = SZ_4M
+ }, {
+ .name = "root",
+ .offset = MTDPART_OFS_NXTBLK,
+ .size = MTDPART_SIZ_FULL
+ },
+};
+
+static struct mv643xx_eth_platform_data openrd_base_ge00_data = {
+ .phy_addr = MV643XX_ETH_PHY_ADDR(8),
+};
+
+static struct mv_sata_platform_data openrd_base_sata_data = {
+ .n_ports = 2,
+};
+
+static struct mvsdio_platform_data openrd_base_mvsdio_data = {
+ .gpio_card_detect = 29, /* MPP29 used as SD card detect */
+};
+
+static unsigned int openrd_base_mpp_config[] __initdata = {
+ MPP29_GPIO,
+ 0
+};
+
+static void __init openrd_base_init(void)
+{
+ /*
+ * Basic setup. Needs to be called early.
+ */
+ kirkwood_init();
+ kirkwood_mpp_conf(openrd_base_mpp_config);
+
+ kirkwood_uart0_init();
+ kirkwood_nand_init(ARRAY_AND_SIZE(openrd_base_nand_parts), 25);
+
+ kirkwood_ehci_init();
+
+ kirkwood_ge00_init(&openrd_base_ge00_data);
+ kirkwood_sata_init(&openrd_base_sata_data);
+ kirkwood_sdio_init(&openrd_base_mvsdio_data);
+}
+
+MACHINE_START(OPENRD_BASE, "Marvell OpenRD Base Board")
+ /* Maintainer: Dhaval Vasa <dhaval.vasa@einfochips.com> */
+ .phys_io = KIRKWOOD_REGS_PHYS_BASE,
+ .io_pg_offst = ((KIRKWOOD_REGS_VIRT_BASE) >> 18) & 0xfffc,
+ .boot_params = 0x00000100,
+ .init_machine = openrd_base_init,
+ .map_io = kirkwood_map_io,
+ .init_irq = kirkwood_init_irq,
+ .timer = &kirkwood_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx1/clock.c b/arch/arm/mach-mx1/clock.c
index 0d0f306851d..d1b588519ad 100644
--- a/arch/arm/mach-mx1/clock.c
+++ b/arch/arm/mach-mx1/clock.c
@@ -18,11 +18,14 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/list.h>
#include <linux/math64.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <asm/clkdev.h>
+
#include <mach/clock.h>
#include <mach/hardware.h>
#include <mach/common.h>
@@ -94,7 +97,6 @@ static unsigned long clk16m_get_rate(struct clk *clk)
}
static struct clk clk16m = {
- .name = "CLK16M",
.get_rate = clk16m_get_rate,
.enable = _clk_enable,
.enable_reg = CCM_CSCR,
@@ -111,7 +113,6 @@ static unsigned long clk32_get_rate(struct clk *clk)
}
static struct clk clk32 = {
- .name = "CLK32",
.get_rate = clk32_get_rate,
};
@@ -121,7 +122,6 @@ static unsigned long clk32_premult_get_rate(struct clk *clk)
}
static struct clk clk32_premult = {
- .name = "CLK32_premultiplier",
.parent = &clk32,
.get_rate = clk32_premult_get_rate,
};
@@ -156,7 +156,6 @@ static int prem_clk_set_parent(struct clk *clk, struct clk *parent)
}
static struct clk prem_clk = {
- .name = "prem_clk",
.set_parent = prem_clk_set_parent,
};
@@ -167,7 +166,6 @@ static unsigned long system_clk_get_rate(struct clk *clk)
}
static struct clk system_clk = {
- .name = "system_clk",
.parent = &prem_clk,
.get_rate = system_clk_get_rate,
};
@@ -179,7 +177,6 @@ static unsigned long mcu_clk_get_rate(struct clk *clk)
}
static struct clk mcu_clk = {
- .name = "mcu_clk",
.parent = &clk32_premult,
.get_rate = mcu_clk_get_rate,
};
@@ -195,7 +192,6 @@ static unsigned long fclk_get_rate(struct clk *clk)
}
static struct clk fclk = {
- .name = "fclk",
.parent = &mcu_clk,
.get_rate = fclk_get_rate,
};
@@ -238,7 +234,6 @@ static int hclk_set_rate(struct clk *clk, unsigned long rate)
}
static struct clk hclk = {
- .name = "hclk",
.parent = &system_clk,
.get_rate = hclk_get_rate,
.round_rate = hclk_round_rate,
@@ -280,7 +275,6 @@ static int clk48m_set_rate(struct clk *clk, unsigned long rate)
}
static struct clk clk48m = {
- .name = "CLK48M",
.parent = &system_clk,
.get_rate = clk48m_get_rate,
.round_rate = clk48m_round_rate,
@@ -400,21 +394,18 @@ static int perclk3_set_rate(struct clk *clk, unsigned long rate)
static struct clk perclk[] = {
{
- .name = "perclk",
.id = 0,
.parent = &system_clk,
.get_rate = perclk1_get_rate,
.round_rate = perclk1_round_rate,
.set_rate = perclk1_set_rate,
}, {
- .name = "perclk",
.id = 1,
.parent = &system_clk,
.get_rate = perclk2_get_rate,
.round_rate = perclk2_round_rate,
.set_rate = perclk2_set_rate,
}, {
- .name = "perclk",
.id = 2,
.parent = &system_clk,
.get_rate = perclk3_get_rate,
@@ -457,12 +448,10 @@ static int clko_set_parent(struct clk *clk, struct clk *parent)
}
static struct clk clko_clk = {
- .name = "clko_clk",
.set_parent = clko_set_parent,
};
static struct clk dma_clk = {
- .name = "dma",
.parent = &hclk,
.round_rate = _clk_parent_round_rate,
.set_rate = _clk_parent_set_rate,
@@ -473,7 +462,6 @@ static struct clk dma_clk = {
};
static struct clk csi_clk = {
- .name = "csi_clk",
.parent = &hclk,
.round_rate = _clk_parent_round_rate,
.set_rate = _clk_parent_set_rate,
@@ -484,7 +472,6 @@ static struct clk csi_clk = {
};
static struct clk mma_clk = {
- .name = "mma_clk",
.parent = &hclk,
.round_rate = _clk_parent_round_rate,
.set_rate = _clk_parent_set_rate,
@@ -495,7 +482,6 @@ static struct clk mma_clk = {
};
static struct clk usbd_clk = {
- .name = "usbd_clk",
.parent = &clk48m,
.round_rate = _clk_parent_round_rate,
.set_rate = _clk_parent_set_rate,
@@ -506,99 +492,85 @@ static struct clk usbd_clk = {
};
static struct clk gpt_clk = {
- .name = "gpt_clk",
.parent = &perclk[0],
.round_rate = _clk_parent_round_rate,
.set_rate = _clk_parent_set_rate,
};
static struct clk uart_clk = {
- .name = "uart",
.parent = &perclk[0],
.round_rate = _clk_parent_round_rate,
.set_rate = _clk_parent_set_rate,
};
static struct clk i2c_clk = {
- .name = "i2c_clk",
.parent = &hclk,
.round_rate = _clk_parent_round_rate,
.set_rate = _clk_parent_set_rate,
};
static struct clk spi_clk = {
- .name = "spi_clk",
.parent = &perclk[1],
.round_rate = _clk_parent_round_rate,
.set_rate = _clk_parent_set_rate,
};
static struct clk sdhc_clk = {
- .name = "sdhc_clk",
.parent = &perclk[1],
.round_rate = _clk_parent_round_rate,
.set_rate = _clk_parent_set_rate,
};
static struct clk lcdc_clk = {
- .name = "lcdc_clk",
.parent = &perclk[1],
.round_rate = _clk_parent_round_rate,
.set_rate = _clk_parent_set_rate,
};
static struct clk mshc_clk = {
- .name = "mshc_clk",
.parent = &hclk,
.round_rate = _clk_parent_round_rate,
.set_rate = _clk_parent_set_rate,
};
static struct clk ssi_clk = {
- .name = "ssi_clk",
.parent = &perclk[2],
.round_rate = _clk_parent_round_rate,
.set_rate = _clk_parent_set_rate,
};
static struct clk rtc_clk = {
- .name = "rtc_clk",
.parent = &clk32,
};
-static struct clk *mxc_clks[] = {
- &clk16m,
- &clk32,
- &clk32_premult,
- &prem_clk,
- &system_clk,
- &mcu_clk,
- &fclk,
- &hclk,
- &clk48m,
- &perclk[0],
- &perclk[1],
- &perclk[2],
- &clko_clk,
- &dma_clk,
- &csi_clk,
- &mma_clk,
- &usbd_clk,
- &gpt_clk,
- &uart_clk,
- &i2c_clk,
- &spi_clk,
- &sdhc_clk,
- &lcdc_clk,
- &mshc_clk,
- &ssi_clk,
- &rtc_clk,
+#define _REGISTER_CLOCK(d, n, c) \
+ { \
+ .dev_id = d, \
+ .con_id = n, \
+ .clk = &c, \
+ },
+static struct clk_lookup lookups[] __initdata = {
+ _REGISTER_CLOCK(NULL, "dma", dma_clk)
+ _REGISTER_CLOCK("mx1-camera.0", NULL, csi_clk)
+ _REGISTER_CLOCK(NULL, "mma", mma_clk)
+ _REGISTER_CLOCK("imx_udc.0", NULL, usbd_clk)
+ _REGISTER_CLOCK(NULL, "gpt", gpt_clk)
+ _REGISTER_CLOCK("imx-uart.0", NULL, uart_clk)
+ _REGISTER_CLOCK("imx-uart.1", NULL, uart_clk)
+ _REGISTER_CLOCK("imx-uart.2", NULL, uart_clk)
+ _REGISTER_CLOCK("imx-i2c.0", NULL, i2c_clk)
+ _REGISTER_CLOCK("spi_imx.0", NULL, spi_clk)
+ _REGISTER_CLOCK("imx-mmc.0", NULL, sdhc_clk)
+ _REGISTER_CLOCK("imx-fb.0", NULL, lcdc_clk)
+ _REGISTER_CLOCK(NULL, "mshc", mshc_clk)
+ _REGISTER_CLOCK(NULL, "ssi", ssi_clk)
+ _REGISTER_CLOCK("mxc_rtc.0", NULL, rtc_clk)
};
int __init mx1_clocks_init(unsigned long fref)
{
- struct clk **clkp;
unsigned int reg;
+ int i;
/* disable clocks we are able to */
__raw_writel(0, SCM_GCCR);
@@ -620,13 +592,13 @@ int __init mx1_clocks_init(unsigned long fref)
reg = (reg & CCM_CSCR_CLKO_MASK) >> CCM_CSCR_CLKO_OFFSET;
clko_clk.parent = (struct clk *)clko_clocks[reg];
- for (clkp = mxc_clks; clkp < mxc_clks + ARRAY_SIZE(mxc_clks); clkp++)
- clk_register(*clkp);
+ for (i = 0; i < ARRAY_SIZE(lookups); i++)
+ clkdev_add(&lookups[i]);
clk_enable(&hclk);
clk_enable(&fclk);
- mxc_timer_init(&gpt_clk);
+ mxc_timer_init(&gpt_clk, IO_ADDRESS(TIM1_BASE_ADDR), TIM1_INT);
return 0;
}
diff --git a/arch/arm/mach-mx1/devices.c b/arch/arm/mach-mx1/devices.c
index 76d1ffb4807..b6be29d1cb0 100644
--- a/arch/arm/mach-mx1/devices.c
+++ b/arch/arm/mach-mx1/devices.c
@@ -29,12 +29,11 @@
#include "devices.h"
static struct resource imx_csi_resources[] = {
- [0] = {
+ {
.start = 0x00224000,
.end = 0x00224010,
.flags = IORESOURCE_MEM,
- },
- [1] = {
+ }, {
.start = CSI_INT,
.end = CSI_INT,
.flags = IORESOURCE_IRQ,
@@ -55,12 +54,11 @@ struct platform_device imx_csi_device = {
};
static struct resource imx_i2c_resources[] = {
- [0] = {
+ {
.start = 0x00217000,
.end = 0x00217010,
.flags = IORESOURCE_MEM,
- },
- [1] = {
+ }, {
.start = I2C_INT,
.end = I2C_INT,
.flags = IORESOURCE_IRQ,
@@ -75,22 +73,19 @@ struct platform_device imx_i2c_device = {
};
static struct resource imx_uart1_resources[] = {
- [0] = {
+ {
.start = UART1_BASE_ADDR,
.end = UART1_BASE_ADDR + 0xD0,
.flags = IORESOURCE_MEM,
- },
- [1] = {
+ }, {
.start = UART1_MINT_RX,
.end = UART1_MINT_RX,
.flags = IORESOURCE_IRQ,
- },
- [2] = {
+ }, {
.start = UART1_MINT_TX,
.end = UART1_MINT_TX,
.flags = IORESOURCE_IRQ,
- },
- [3] = {
+ }, {
.start = UART1_MINT_RTS,
.end = UART1_MINT_RTS,
.flags = IORESOURCE_IRQ,
@@ -105,22 +100,19 @@ struct platform_device imx_uart1_device = {
};
static struct resource imx_uart2_resources[] = {
- [0] = {
+ {
.start = UART2_BASE_ADDR,
.end = UART2_BASE_ADDR + 0xD0,
.flags = IORESOURCE_MEM,
- },
- [1] = {
+ }, {
.start = UART2_MINT_RX,
.end = UART2_MINT_RX,
.flags = IORESOURCE_IRQ,
- },
- [2] = {
+ }, {
.start = UART2_MINT_TX,
.end = UART2_MINT_TX,
.flags = IORESOURCE_IRQ,
- },
- [3] = {
+ }, {
.start = UART2_MINT_RTS,
.end = UART2_MINT_RTS,
.flags = IORESOURCE_IRQ,
@@ -135,17 +127,15 @@ struct platform_device imx_uart2_device = {
};
static struct resource imx_rtc_resources[] = {
- [0] = {
+ {
.start = 0x00204000,
.end = 0x00204024,
.flags = IORESOURCE_MEM,
- },
- [1] = {
+ }, {
.start = RTC_INT,
.end = RTC_INT,
.flags = IORESOURCE_IRQ,
- },
- [2] = {
+ }, {
.start = RTC_SAMINT,
.end = RTC_SAMINT,
.flags = IORESOURCE_IRQ,
@@ -160,12 +150,11 @@ struct platform_device imx_rtc_device = {
};
static struct resource imx_wdt_resources[] = {
- [0] = {
+ {
.start = 0x00201000,
.end = 0x00201008,
.flags = IORESOURCE_MEM,
- },
- [1] = {
+ }, {
.start = WDT_INT,
.end = WDT_INT,
.flags = IORESOURCE_IRQ,
@@ -180,42 +169,35 @@ struct platform_device imx_wdt_device = {
};
static struct resource imx_usb_resources[] = {
- [0] = {
+ {
.start = 0x00212000,
.end = 0x00212148,
.flags = IORESOURCE_MEM,
- },
- [1] = {
+ }, {
.start = USBD_INT0,
.end = USBD_INT0,
.flags = IORESOURCE_IRQ,
- },
- [2] = {
+ }, {
.start = USBD_INT1,
.end = USBD_INT1,
.flags = IORESOURCE_IRQ,
- },
- [3] = {
+ }, {
.start = USBD_INT2,
.end = USBD_INT2,
.flags = IORESOURCE_IRQ,
- },
- [4] = {
+ }, {
.start = USBD_INT3,
.end = USBD_INT3,
.flags = IORESOURCE_IRQ,
- },
- [5] = {
+ }, {
.start = USBD_INT4,
.end = USBD_INT4,
.flags = IORESOURCE_IRQ,
- },
- [6] = {
+ }, {
.start = USBD_INT5,
.end = USBD_INT5,
.flags = IORESOURCE_IRQ,
- },
- [7] = {
+ }, {
.start = USBD_INT6,
.end = USBD_INT6,
.flags = IORESOURCE_IRQ,
@@ -231,29 +213,26 @@ struct platform_device imx_usb_device = {
/* GPIO port description */
static struct mxc_gpio_port imx_gpio_ports[] = {
- [0] = {
+ {
.chip.label = "gpio-0",
.base = (void __iomem *)IO_ADDRESS(GPIO_BASE_ADDR),
.irq = GPIO_INT_PORTA,
- .virtual_irq_start = MXC_GPIO_IRQ_START
- },
- [1] = {
+ .virtual_irq_start = MXC_GPIO_IRQ_START,
+ }, {
.chip.label = "gpio-1",
.base = (void __iomem *)IO_ADDRESS(GPIO_BASE_ADDR + 0x100),
.irq = GPIO_INT_PORTB,
- .virtual_irq_start = MXC_GPIO_IRQ_START + 32
- },
- [2] = {
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 32,
+ }, {
.chip.label = "gpio-2",
.base = (void __iomem *)IO_ADDRESS(GPIO_BASE_ADDR + 0x200),
.irq = GPIO_INT_PORTC,
- .virtual_irq_start = MXC_GPIO_IRQ_START + 64
- },
- [3] = {
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 64,
+ }, {
.chip.label = "gpio-3",
.base = (void __iomem *)IO_ADDRESS(GPIO_BASE_ADDR + 0x300),
.irq = GPIO_INT_PORTD,
- .virtual_irq_start = MXC_GPIO_IRQ_START + 96
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 96,
}
};
diff --git a/arch/arm/mach-mx1/generic.c b/arch/arm/mach-mx1/generic.c
index 7622c9b38c9..7f9fc1034c0 100644
--- a/arch/arm/mach-mx1/generic.c
+++ b/arch/arm/mach-mx1/generic.c
@@ -41,6 +41,13 @@ static struct map_desc imx_io_desc[] __initdata = {
void __init mx1_map_io(void)
{
mxc_set_cpu_type(MXC_CPU_MX1);
+ mxc_arch_reset_init(IO_ADDRESS(WDT_BASE_ADDR));
iotable_init(imx_io_desc, ARRAY_SIZE(imx_io_desc));
}
+
+void __init mx1_init_irq(void)
+{
+ mxc_init_irq(IO_ADDRESS(AVIC_BASE_ADDR));
+}
+
diff --git a/arch/arm/mach-mx1/mx1ads.c b/arch/arm/mach-mx1/mx1ads.c
index e5b0c0a83c3..30f04e56faf 100644
--- a/arch/arm/mach-mx1/mx1ads.c
+++ b/arch/arm/mach-mx1/mx1ads.c
@@ -104,12 +104,10 @@ static struct imxi2c_platform_data mx1ads_i2c_data = {
static struct i2c_board_info mx1ads_i2c_devices[] = {
{
- I2C_BOARD_INFO("pcf857x", 0x22),
- .type = "pcf8575",
+ I2C_BOARD_INFO("pcf8575", 0x22),
.platform_data = &pcf857x_data[0],
}, {
- I2C_BOARD_INFO("pcf857x", 0x24),
- .type = "pcf8575",
+ I2C_BOARD_INFO("pcf8575", 0x24),
.platform_data = &pcf857x_data[1],
},
};
@@ -151,7 +149,7 @@ MACHINE_START(MX1ADS, "Freescale MX1ADS")
.io_pg_offst = (IMX_IO_BASE >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx1_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx1_init_irq,
.timer = &mx1ads_timer,
.init_machine = mx1ads_init,
MACHINE_END
@@ -161,7 +159,7 @@ MACHINE_START(MXLADS, "Freescale MXLADS")
.io_pg_offst = (IMX_IO_BASE >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx1_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx1_init_irq,
.timer = &mx1ads_timer,
.init_machine = mx1ads_init,
MACHINE_END
diff --git a/arch/arm/mach-mx1/scb9328.c b/arch/arm/mach-mx1/scb9328.c
index 20e0b5bcdff..325d98df605 100644
--- a/arch/arm/mach-mx1/scb9328.c
+++ b/arch/arm/mach-mx1/scb9328.c
@@ -68,22 +68,20 @@ static struct dm9000_plat_data dm9000_platdata = {
* to gain access to address latch registers and the data path.
*/
static struct resource dm9000x_resources[] = {
- [0] = {
+ {
.name = "address area",
.start = IMX_CS5_PHYS,
.end = IMX_CS5_PHYS + 1,
- .flags = IORESOURCE_MEM /* address access */
- },
- [1] = {
+ .flags = IORESOURCE_MEM, /* address access */
+ }, {
.name = "data area",
.start = IMX_CS5_PHYS + 4,
.end = IMX_CS5_PHYS + 5,
- .flags = IORESOURCE_MEM /* data access */
- },
- [2] = {
+ .flags = IORESOURCE_MEM, /* data access */
+ }, {
.start = IRQ_GPIOC(3),
.end = IRQ_GPIOC(3),
- .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
},
};
@@ -154,7 +152,7 @@ MACHINE_START(SCB9328, "Synertronixx scb9328")
.io_pg_offst = ((0xe0200000) >> 18) & 0xfffc,
.boot_params = 0x08000100,
.map_io = mx1_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx1_init_irq,
.timer = &scb9328_timer,
.init_machine = scb9328_init,
MACHINE_END
diff --git a/arch/arm/mach-mx2/Kconfig b/arch/arm/mach-mx2/Kconfig
index c77da586b71..c8a2eac4d13 100644
--- a/arch/arm/mach-mx2/Kconfig
+++ b/arch/arm/mach-mx2/Kconfig
@@ -53,6 +53,34 @@ config MACH_PCM970_BASEBOARD
endchoice
+config MACH_EUKREA_CPUIMX27
+ bool "Eukrea CPUIMX27 module"
+ depends on MACH_MX27
+ help
+ Include support for Eukrea CPUIMX27 platform. This includes
+ specific configurations for the module and its peripherals.
+
+config MACH_EUKREA_CPUIMX27_USESDHC2
+ bool "CPUIMX27 integrates SDHC2 module"
+ depends on MACH_EUKREA_CPUIMX27
+ help
+ This adds support for the internal SDHC2 used on CPUIMX27 used
+ for wifi or eMMC.
+
+choice
+ prompt "Baseboard"
+ depends on MACH_EUKREA_CPUIMX27
+ default MACH_EUKREA_MBIMX27_BASEBOARD
+
+config MACH_EUKREA_MBIMX27_BASEBOARD
+ prompt "Eukrea MBIMX27 development board"
+ bool
+ help
+ This adds board specific devices that can be found on Eukrea's
+ MBIMX27 evaluation board.
+
+endchoice
+
config MACH_MX27_3DS
bool "MX27PDK platform"
depends on MACH_MX27
@@ -67,4 +95,11 @@ config MACH_MX27LITE
Include support for MX27 LITEKIT platform. This includes specific
configurations for the board and its peripherals.
+config MACH_PCA100
+ bool "Phytec phyCARD-s (pca100)"
+ depends on MACH_MX27
+ help
+ Include support for phyCARD-s (aka pca100) platform. This
+ includes specific configurations for the module and its peripherals.
+
endif
diff --git a/arch/arm/mach-mx2/Makefile b/arch/arm/mach-mx2/Makefile
index b9b1cca4e9b..19560f04563 100644
--- a/arch/arm/mach-mx2/Makefile
+++ b/arch/arm/mach-mx2/Makefile
@@ -17,4 +17,7 @@ obj-$(CONFIG_MACH_PCM038) += pcm038.o
obj-$(CONFIG_MACH_PCM970_BASEBOARD) += pcm970-baseboard.o
obj-$(CONFIG_MACH_MX27_3DS) += mx27pdk.o
obj-$(CONFIG_MACH_MX27LITE) += mx27lite.o
+obj-$(CONFIG_MACH_EUKREA_CPUIMX27) += eukrea_cpuimx27.o
+obj-$(CONFIG_MACH_EUKREA_MBIMX27_BASEBOARD) += eukrea_mbimx27-baseboard.o
+obj-$(CONFIG_MACH_PCA100) += pca100.o
diff --git a/arch/arm/mach-mx2/clock_imx21.c b/arch/arm/mach-mx2/clock_imx21.c
index 0850fb88ec1..eede79855f4 100644
--- a/arch/arm/mach-mx2/clock_imx21.c
+++ b/arch/arm/mach-mx2/clock_imx21.c
@@ -1004,6 +1004,6 @@ int __init mx21_clocks_init(unsigned long lref, unsigned long href)
clk_enable(&uart_clk[0]);
#endif
- mxc_timer_init(&gpt_clk[0]);
+ mxc_timer_init(&gpt_clk[0], IO_ADDRESS(GPT1_BASE_ADDR), MXC_INT_GPT1);
return 0;
}
diff --git a/arch/arm/mach-mx2/clock_imx27.c b/arch/arm/mach-mx2/clock_imx27.c
index 2c971442f3f..4089951acb4 100644
--- a/arch/arm/mach-mx2/clock_imx27.c
+++ b/arch/arm/mach-mx2/clock_imx27.c
@@ -643,7 +643,14 @@ static struct clk_lookup lookups[] = {
_REGISTER_CLOCK(NULL, "cspi3", cspi3_clk)
_REGISTER_CLOCK("imx-fb.0", NULL, lcdc_clk)
_REGISTER_CLOCK(NULL, "csi", csi_clk)
- _REGISTER_CLOCK(NULL, "usb", usb_clk)
+ _REGISTER_CLOCK("fsl-usb2-udc", "usb", usb_clk)
+ _REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", usb_clk1)
+ _REGISTER_CLOCK("mxc-ehci.0", "usb", usb_clk)
+ _REGISTER_CLOCK("mxc-ehci.0", "usb_ahb", usb_clk1)
+ _REGISTER_CLOCK("mxc-ehci.1", "usb", usb_clk)
+ _REGISTER_CLOCK("mxc-ehci.1", "usb_ahb", usb_clk1)
+ _REGISTER_CLOCK("mxc-ehci.2", "usb", usb_clk)
+ _REGISTER_CLOCK("mxc-ehci.2", "usb_ahb", usb_clk1)
_REGISTER_CLOCK(NULL, "ssi1", ssi1_clk)
_REGISTER_CLOCK(NULL, "ssi2", ssi2_clk)
_REGISTER_CLOCK("mxc_nand.0", NULL, nfc_clk)
@@ -748,7 +755,7 @@ int __init mx27_clocks_init(unsigned long fref)
clk_enable(&uart1_clk);
#endif
- mxc_timer_init(&gpt1_clk);
+ mxc_timer_init(&gpt1_clk, IO_ADDRESS(GPT1_BASE_ADDR), MXC_INT_GPT1);
return 0;
}
diff --git a/arch/arm/mach-mx2/devices.c b/arch/arm/mach-mx2/devices.c
index a0f1b367432..50199aff014 100644
--- a/arch/arm/mach-mx2/devices.c
+++ b/arch/arm/mach-mx2/devices.c
@@ -40,45 +40,87 @@
#include "devices.h"
/*
- * Resource definition for the MXC IrDA
+ * SPI master controller
+ *
+ * - i.MX1: 2 channel (slighly different register setting)
+ * - i.MX21: 2 channel
+ * - i.MX27: 3 channel
*/
-static struct resource mxc_irda_resources[] = {
- [0] = {
- .start = UART3_BASE_ADDR,
- .end = UART3_BASE_ADDR + SZ_4K - 1,
- .flags = IORESOURCE_MEM,
+static struct resource mxc_spi_resources0[] = {
+ {
+ .start = CSPI1_BASE_ADDR,
+ .end = CSPI1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_CSPI1,
+ .end = MXC_INT_CSPI1,
+ .flags = IORESOURCE_IRQ,
},
- [1] = {
- .start = MXC_INT_UART3,
- .end = MXC_INT_UART3,
- .flags = IORESOURCE_IRQ,
+};
+
+static struct resource mxc_spi_resources1[] = {
+ {
+ .start = CSPI2_BASE_ADDR,
+ .end = CSPI2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_CSPI2,
+ .end = MXC_INT_CSPI2,
+ .flags = IORESOURCE_IRQ,
},
};
-/* Platform Data for MXC IrDA */
-struct platform_device mxc_irda_device = {
- .name = "mxc_irda",
+#ifdef CONFIG_MACH_MX27
+static struct resource mxc_spi_resources2[] = {
+ {
+ .start = CSPI3_BASE_ADDR,
+ .end = CSPI3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_CSPI3,
+ .end = MXC_INT_CSPI3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+#endif
+
+struct platform_device mxc_spi_device0 = {
+ .name = "spi_imx",
.id = 0,
- .num_resources = ARRAY_SIZE(mxc_irda_resources),
- .resource = mxc_irda_resources,
+ .num_resources = ARRAY_SIZE(mxc_spi_resources0),
+ .resource = mxc_spi_resources0,
+};
+
+struct platform_device mxc_spi_device1 = {
+ .name = "spi_imx",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(mxc_spi_resources1),
+ .resource = mxc_spi_resources1,
+};
+
+#ifdef CONFIG_MACH_MX27
+struct platform_device mxc_spi_device2 = {
+ .name = "spi_imx",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(mxc_spi_resources2),
+ .resource = mxc_spi_resources2,
};
+#endif
/*
* General Purpose Timer
- * - i.MX1: 2 timer (slighly different register handling)
- * - i.MX21: 3 timer
- * - i.MX27: 6 timer
+ * - i.MX21: 3 timers
+ * - i.MX27: 6 timers
*/
/* We use gpt0 as system timer, so do not add a device for this one */
static struct resource timer1_resources[] = {
- [0] = {
+ {
.start = GPT2_BASE_ADDR,
.end = GPT2_BASE_ADDR + 0x17,
- .flags = IORESOURCE_MEM
- },
- [1] = {
+ .flags = IORESOURCE_MEM,
+ }, {
.start = MXC_INT_GPT2,
.end = MXC_INT_GPT2,
.flags = IORESOURCE_IRQ,
@@ -89,16 +131,15 @@ struct platform_device mxc_gpt1 = {
.name = "imx_gpt",
.id = 1,
.num_resources = ARRAY_SIZE(timer1_resources),
- .resource = timer1_resources
+ .resource = timer1_resources,
};
static struct resource timer2_resources[] = {
- [0] = {
+ {
.start = GPT3_BASE_ADDR,
.end = GPT3_BASE_ADDR + 0x17,
- .flags = IORESOURCE_MEM
- },
- [1] = {
+ .flags = IORESOURCE_MEM,
+ }, {
.start = MXC_INT_GPT3,
.end = MXC_INT_GPT3,
.flags = IORESOURCE_IRQ,
@@ -109,17 +150,16 @@ struct platform_device mxc_gpt2 = {
.name = "imx_gpt",
.id = 2,
.num_resources = ARRAY_SIZE(timer2_resources),
- .resource = timer2_resources
+ .resource = timer2_resources,
};
#ifdef CONFIG_MACH_MX27
static struct resource timer3_resources[] = {
- [0] = {
+ {
.start = GPT4_BASE_ADDR,
.end = GPT4_BASE_ADDR + 0x17,
- .flags = IORESOURCE_MEM
- },
- [1] = {
+ .flags = IORESOURCE_MEM,
+ }, {
.start = MXC_INT_GPT4,
.end = MXC_INT_GPT4,
.flags = IORESOURCE_IRQ,
@@ -130,16 +170,15 @@ struct platform_device mxc_gpt3 = {
.name = "imx_gpt",
.id = 3,
.num_resources = ARRAY_SIZE(timer3_resources),
- .resource = timer3_resources
+ .resource = timer3_resources,
};
static struct resource timer4_resources[] = {
- [0] = {
+ {
.start = GPT5_BASE_ADDR,
.end = GPT5_BASE_ADDR + 0x17,
- .flags = IORESOURCE_MEM
- },
- [1] = {
+ .flags = IORESOURCE_MEM,
+ }, {
.start = MXC_INT_GPT5,
.end = MXC_INT_GPT5,
.flags = IORESOURCE_IRQ,
@@ -150,16 +189,15 @@ struct platform_device mxc_gpt4 = {
.name = "imx_gpt",
.id = 4,
.num_resources = ARRAY_SIZE(timer4_resources),
- .resource = timer4_resources
+ .resource = timer4_resources,
};
static struct resource timer5_resources[] = {
- [0] = {
+ {
.start = GPT6_BASE_ADDR,
.end = GPT6_BASE_ADDR + 0x17,
- .flags = IORESOURCE_MEM
- },
- [1] = {
+ .flags = IORESOURCE_MEM,
+ }, {
.start = MXC_INT_GPT6,
.end = MXC_INT_GPT6,
.flags = IORESOURCE_IRQ,
@@ -170,7 +208,7 @@ struct platform_device mxc_gpt5 = {
.name = "imx_gpt",
.id = 5,
.num_resources = ARRAY_SIZE(timer5_resources),
- .resource = timer5_resources
+ .resource = timer5_resources,
};
#endif
@@ -214,11 +252,11 @@ static struct resource mxc_nand_resources[] = {
{
.start = NFC_BASE_ADDR,
.end = NFC_BASE_ADDR + 0xfff,
- .flags = IORESOURCE_MEM
+ .flags = IORESOURCE_MEM,
}, {
.start = MXC_INT_NANDFC,
.end = MXC_INT_NANDFC,
- .flags = IORESOURCE_IRQ
+ .flags = IORESOURCE_IRQ,
},
};
@@ -240,8 +278,7 @@ static struct resource mxc_fb[] = {
.start = LCDC_BASE_ADDR,
.end = LCDC_BASE_ADDR + 0xFFF,
.flags = IORESOURCE_MEM,
- },
- {
+ }, {
.start = MXC_INT_LCDC,
.end = MXC_INT_LCDC,
.flags = IORESOURCE_IRQ,
@@ -264,11 +301,11 @@ static struct resource mxc_fec_resources[] = {
{
.start = FEC_BASE_ADDR,
.end = FEC_BASE_ADDR + 0xfff,
- .flags = IORESOURCE_MEM
+ .flags = IORESOURCE_MEM,
}, {
.start = MXC_INT_FEC,
.end = MXC_INT_FEC,
- .flags = IORESOURCE_IRQ
+ .flags = IORESOURCE_IRQ,
},
};
@@ -281,15 +318,14 @@ struct platform_device mxc_fec_device = {
#endif
static struct resource mxc_i2c_1_resources[] = {
- [0] = {
+ {
.start = I2C_BASE_ADDR,
.end = I2C_BASE_ADDR + 0x0fff,
- .flags = IORESOURCE_MEM
- },
- [1] = {
+ .flags = IORESOURCE_MEM,
+ }, {
.start = MXC_INT_I2C,
.end = MXC_INT_I2C,
- .flags = IORESOURCE_IRQ
+ .flags = IORESOURCE_IRQ,
}
};
@@ -297,20 +333,19 @@ struct platform_device mxc_i2c_device0 = {
.name = "imx-i2c",
.id = 0,
.num_resources = ARRAY_SIZE(mxc_i2c_1_resources),
- .resource = mxc_i2c_1_resources
+ .resource = mxc_i2c_1_resources,
};
#ifdef CONFIG_MACH_MX27
static struct resource mxc_i2c_2_resources[] = {
- [0] = {
+ {
.start = I2C2_BASE_ADDR,
.end = I2C2_BASE_ADDR + 0x0fff,
- .flags = IORESOURCE_MEM
- },
- [1] = {
+ .flags = IORESOURCE_MEM,
+ }, {
.start = MXC_INT_I2C2,
.end = MXC_INT_I2C2,
- .flags = IORESOURCE_IRQ
+ .flags = IORESOURCE_IRQ,
}
};
@@ -318,17 +353,16 @@ struct platform_device mxc_i2c_device1 = {
.name = "imx-i2c",
.id = 1,
.num_resources = ARRAY_SIZE(mxc_i2c_2_resources),
- .resource = mxc_i2c_2_resources
+ .resource = mxc_i2c_2_resources,
};
#endif
static struct resource mxc_pwm_resources[] = {
- [0] = {
+ {
.start = PWM_BASE_ADDR,
.end = PWM_BASE_ADDR + 0x0fff,
- .flags = IORESOURCE_MEM
- },
- [1] = {
+ .flags = IORESOURCE_MEM,
+ }, {
.start = MXC_INT_PWM,
.end = MXC_INT_PWM,
.flags = IORESOURCE_IRQ,
@@ -339,28 +373,26 @@ struct platform_device mxc_pwm_device = {
.name = "mxc_pwm",
.id = 0,
.num_resources = ARRAY_SIZE(mxc_pwm_resources),
- .resource = mxc_pwm_resources
+ .resource = mxc_pwm_resources,
};
/*
* Resource definition for the MXC SDHC
*/
static struct resource mxc_sdhc1_resources[] = {
- [0] = {
- .start = SDHC1_BASE_ADDR,
- .end = SDHC1_BASE_ADDR + SZ_4K - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = MXC_INT_SDHC1,
- .end = MXC_INT_SDHC1,
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
- .start = DMA_REQ_SDHC1,
- .end = DMA_REQ_SDHC1,
- .flags = IORESOURCE_DMA
- },
+ {
+ .start = SDHC1_BASE_ADDR,
+ .end = SDHC1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_SDHC1,
+ .end = MXC_INT_SDHC1,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = DMA_REQ_SDHC1,
+ .end = DMA_REQ_SDHC1,
+ .flags = IORESOURCE_DMA,
+ },
};
static u64 mxc_sdhc1_dmamask = 0xffffffffUL;
@@ -377,21 +409,19 @@ struct platform_device mxc_sdhc_device0 = {
};
static struct resource mxc_sdhc2_resources[] = {
- [0] = {
- .start = SDHC2_BASE_ADDR,
- .end = SDHC2_BASE_ADDR + SZ_4K - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = MXC_INT_SDHC2,
- .end = MXC_INT_SDHC2,
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
- .start = DMA_REQ_SDHC2,
- .end = DMA_REQ_SDHC2,
- .flags = IORESOURCE_DMA
- },
+ {
+ .start = SDHC2_BASE_ADDR,
+ .end = SDHC2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_SDHC2,
+ .end = MXC_INT_SDHC2,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = DMA_REQ_SDHC2,
+ .end = DMA_REQ_SDHC2,
+ .flags = IORESOURCE_DMA,
+ },
};
static u64 mxc_sdhc2_dmamask = 0xffffffffUL;
@@ -407,35 +437,123 @@ struct platform_device mxc_sdhc_device1 = {
.resource = mxc_sdhc2_resources,
};
+#ifdef CONFIG_MACH_MX27
+static struct resource otg_resources[] = {
+ {
+ .start = OTG_BASE_ADDR,
+ .end = OTG_BASE_ADDR + 0x1ff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_USB3,
+ .end = MXC_INT_USB3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static u64 otg_dmamask = 0xffffffffUL;
+
+/* OTG gadget device */
+struct platform_device mxc_otg_udc_device = {
+ .name = "fsl-usb2-udc",
+ .id = -1,
+ .dev = {
+ .dma_mask = &otg_dmamask,
+ .coherent_dma_mask = 0xffffffffUL,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+/* OTG host */
+struct platform_device mxc_otg_host = {
+ .name = "mxc-ehci",
+ .id = 0,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ .dma_mask = &otg_dmamask,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+/* USB host 1 */
+
+static u64 usbh1_dmamask = 0xffffffffUL;
+
+static struct resource mxc_usbh1_resources[] = {
+ {
+ .start = OTG_BASE_ADDR + 0x200,
+ .end = OTG_BASE_ADDR + 0x3ff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_USB1,
+ .end = MXC_INT_USB1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_usbh1 = {
+ .name = "mxc-ehci",
+ .id = 1,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ .dma_mask = &usbh1_dmamask,
+ },
+ .resource = mxc_usbh1_resources,
+ .num_resources = ARRAY_SIZE(mxc_usbh1_resources),
+};
+
+/* USB host 2 */
+static u64 usbh2_dmamask = 0xffffffffUL;
+
+static struct resource mxc_usbh2_resources[] = {
+ {
+ .start = OTG_BASE_ADDR + 0x400,
+ .end = OTG_BASE_ADDR + 0x5ff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_USB2,
+ .end = MXC_INT_USB2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_usbh2 = {
+ .name = "mxc-ehci",
+ .id = 2,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ .dma_mask = &usbh2_dmamask,
+ },
+ .resource = mxc_usbh2_resources,
+ .num_resources = ARRAY_SIZE(mxc_usbh2_resources),
+};
+#endif
+
/* GPIO port description */
static struct mxc_gpio_port imx_gpio_ports[] = {
- [0] = {
+ {
.chip.label = "gpio-0",
.irq = MXC_INT_GPIO,
.base = IO_ADDRESS(GPIO_BASE_ADDR),
.virtual_irq_start = MXC_GPIO_IRQ_START,
- },
- [1] = {
+ }, {
.chip.label = "gpio-1",
.base = IO_ADDRESS(GPIO_BASE_ADDR + 0x100),
.virtual_irq_start = MXC_GPIO_IRQ_START + 32,
- },
- [2] = {
+ }, {
.chip.label = "gpio-2",
.base = IO_ADDRESS(GPIO_BASE_ADDR + 0x200),
.virtual_irq_start = MXC_GPIO_IRQ_START + 64,
- },
- [3] = {
+ }, {
.chip.label = "gpio-3",
.base = IO_ADDRESS(GPIO_BASE_ADDR + 0x300),
.virtual_irq_start = MXC_GPIO_IRQ_START + 96,
- },
- [4] = {
+ }, {
.chip.label = "gpio-4",
.base = IO_ADDRESS(GPIO_BASE_ADDR + 0x400),
.virtual_irq_start = MXC_GPIO_IRQ_START + 128,
- },
- [5] = {
+ }, {
.chip.label = "gpio-5",
.base = IO_ADDRESS(GPIO_BASE_ADDR + 0x500),
.virtual_irq_start = MXC_GPIO_IRQ_START + 160,
diff --git a/arch/arm/mach-mx2/devices.h b/arch/arm/mach-mx2/devices.h
index 049005bb6aa..d315406d672 100644
--- a/arch/arm/mach-mx2/devices.h
+++ b/arch/arm/mach-mx2/devices.h
@@ -4,7 +4,6 @@ extern struct platform_device mxc_gpt3;
extern struct platform_device mxc_gpt4;
extern struct platform_device mxc_gpt5;
extern struct platform_device mxc_wdt;
-extern struct platform_device mxc_irda_device;
extern struct platform_device mxc_uart_device0;
extern struct platform_device mxc_uart_device1;
extern struct platform_device mxc_uart_device2;
@@ -20,3 +19,11 @@ extern struct platform_device mxc_i2c_device0;
extern struct platform_device mxc_i2c_device1;
extern struct platform_device mxc_sdhc_device0;
extern struct platform_device mxc_sdhc_device1;
+extern struct platform_device mxc_otg_udc_device;
+extern struct platform_device mxc_otg_host;
+extern struct platform_device mxc_usbh1;
+extern struct platform_device mxc_usbh2;
+extern struct platform_device mxc_spi_device0;
+extern struct platform_device mxc_spi_device1;
+extern struct platform_device mxc_spi_device2;
+
diff --git a/arch/arm/mach-mx2/eukrea_cpuimx27.c b/arch/arm/mach-mx2/eukrea_cpuimx27.c
new file mode 100644
index 00000000000..7b187606682
--- /dev/null
+++ b/arch/arm/mach-mx2/eukrea_cpuimx27.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2009 Eric Benard - eric@eukrea.com
+ *
+ * Based on pcm038.c which is :
+ * Copyright 2007 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
+ * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/mtd/plat-ram.h>
+#include <linux/mtd/physmap.h>
+#include <linux/platform_device.h>
+#include <linux/serial_8250.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <asm/mach/map.h>
+
+#include <mach/board-eukrea_cpuimx27.h>
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <mach/i2c.h>
+#include <mach/iomux.h>
+#include <mach/imx-uart.h>
+#include <mach/mxc_nand.h>
+
+#include "devices.h"
+
+static int eukrea_cpuimx27_pins[] = {
+ /* UART1 */
+ PE12_PF_UART1_TXD,
+ PE13_PF_UART1_RXD,
+ PE14_PF_UART1_CTS,
+ PE15_PF_UART1_RTS,
+ /* UART4 */
+ PB26_AF_UART4_RTS,
+ PB28_AF_UART4_TXD,
+ PB29_AF_UART4_CTS,
+ PB31_AF_UART4_RXD,
+ /* FEC */
+ PD0_AIN_FEC_TXD0,
+ PD1_AIN_FEC_TXD1,
+ PD2_AIN_FEC_TXD2,
+ PD3_AIN_FEC_TXD3,
+ PD4_AOUT_FEC_RX_ER,
+ PD5_AOUT_FEC_RXD1,
+ PD6_AOUT_FEC_RXD2,
+ PD7_AOUT_FEC_RXD3,
+ PD8_AF_FEC_MDIO,
+ PD9_AIN_FEC_MDC,
+ PD10_AOUT_FEC_CRS,
+ PD11_AOUT_FEC_TX_CLK,
+ PD12_AOUT_FEC_RXD0,
+ PD13_AOUT_FEC_RX_DV,
+ PD14_AOUT_FEC_RX_CLK,
+ PD15_AOUT_FEC_COL,
+ PD16_AIN_FEC_TX_ER,
+ PF23_AIN_FEC_TX_EN,
+ /* I2C1 */
+ PD17_PF_I2C_DATA,
+ PD18_PF_I2C_CLK,
+ /* SDHC2 */
+ PB4_PF_SD2_D0,
+ PB5_PF_SD2_D1,
+ PB6_PF_SD2_D2,
+ PB7_PF_SD2_D3,
+ PB8_PF_SD2_CMD,
+ PB9_PF_SD2_CLK,
+#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE)
+ /* Quad UART's IRQ */
+ GPIO_PORTD | 22 | GPIO_GPIO | GPIO_IN,
+ GPIO_PORTD | 23 | GPIO_GPIO | GPIO_IN,
+ GPIO_PORTD | 27 | GPIO_GPIO | GPIO_IN,
+ GPIO_PORTD | 30 | GPIO_GPIO | GPIO_IN,
+#endif
+};
+
+static struct physmap_flash_data eukrea_cpuimx27_flash_data = {
+ .width = 2,
+};
+
+static struct resource eukrea_cpuimx27_flash_resource = {
+ .start = 0xc0000000,
+ .end = 0xc3ffffff,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device eukrea_cpuimx27_nor_mtd_device = {
+ .name = "physmap-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &eukrea_cpuimx27_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &eukrea_cpuimx27_flash_resource,
+};
+
+static struct imxuart_platform_data uart_pdata[] = {
+ {
+ .flags = IMXUART_HAVE_RTSCTS,
+ }, {
+ .flags = IMXUART_HAVE_RTSCTS,
+ },
+};
+
+static struct mxc_nand_platform_data eukrea_cpuimx27_nand_board_info = {
+ .width = 1,
+ .hw_ecc = 1,
+};
+
+static struct platform_device *platform_devices[] __initdata = {
+ &eukrea_cpuimx27_nor_mtd_device,
+ &mxc_fec_device,
+};
+
+static struct imxi2c_platform_data eukrea_cpuimx27_i2c_1_data = {
+ .bitrate = 100000,
+};
+
+static struct i2c_board_info eukrea_cpuimx27_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("pcf8563", 0x51),
+ },
+};
+
+#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE)
+static struct plat_serial8250_port serial_platform_data[] = {
+ {
+ .mapbase = (unsigned long)(CS3_BASE_ADDR + 0x200000),
+ .irq = IRQ_GPIOB(23),
+ .uartclk = 14745600,
+ .regshift = 1,
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP,
+ }, {
+ .mapbase = (unsigned long)(CS3_BASE_ADDR + 0x400000),
+ .irq = IRQ_GPIOB(22),
+ .uartclk = 14745600,
+ .regshift = 1,
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP,
+ }, {
+ .mapbase = (unsigned long)(CS3_BASE_ADDR + 0x800000),
+ .irq = IRQ_GPIOB(27),
+ .uartclk = 14745600,
+ .regshift = 1,
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP,
+ }, {
+ .mapbase = (unsigned long)(CS3_BASE_ADDR + 0x1000000),
+ .irq = IRQ_GPIOB(30),
+ .uartclk = 14745600,
+ .regshift = 1,
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP,
+ }, {
+ }
+};
+
+static struct platform_device serial_device = {
+ .name = "serial8250",
+ .id = 0,
+ .dev = {
+ .platform_data = serial_platform_data,
+ },
+};
+#endif
+
+static void __init eukrea_cpuimx27_init(void)
+{
+ mxc_gpio_setup_multiple_pins(eukrea_cpuimx27_pins,
+ ARRAY_SIZE(eukrea_cpuimx27_pins), "CPUIMX27");
+
+ mxc_register_device(&mxc_uart_device0, &uart_pdata[0]);
+
+ mxc_register_device(&mxc_nand_device, &eukrea_cpuimx27_nand_board_info);
+
+ i2c_register_board_info(0, eukrea_cpuimx27_i2c_devices,
+ ARRAY_SIZE(eukrea_cpuimx27_i2c_devices));
+
+ mxc_register_device(&mxc_i2c_device0, &eukrea_cpuimx27_i2c_1_data);
+
+ platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));
+
+#if defined(CONFIG_MACH_EUKREA_CPUIMX27_USESDHC2)
+ /* SDHC2 can be used for Wifi */
+ mxc_register_device(&mxc_sdhc_device1, NULL);
+ /* in which case UART4 is also used for Bluetooth */
+ mxc_register_device(&mxc_uart_device3, &uart_pdata[1]);
+#endif
+
+#if defined(CONFIG_SERIAL_8250) || defined(CONFIG_SERIAL_8250_MODULE)
+ platform_device_register(&serial_device);
+#endif
+
+#ifdef CONFIG_MACH_EUKREA_MBIMX27_BASEBOARD
+ eukrea_mbimx27_baseboard_init();
+#endif
+}
+
+static void __init eukrea_cpuimx27_timer_init(void)
+{
+ mx27_clocks_init(26000000);
+}
+
+static struct sys_timer eukrea_cpuimx27_timer = {
+ .init = eukrea_cpuimx27_timer_init,
+};
+
+MACHINE_START(CPUIMX27, "EUKREA CPUIMX27")
+ .phys_io = AIPI_BASE_ADDR,
+ .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .map_io = mx27_map_io,
+ .init_irq = mx27_init_irq,
+ .init_machine = eukrea_cpuimx27_init,
+ .timer = &eukrea_cpuimx27_timer,
+MACHINE_END
diff --git a/arch/arm/mach-mx2/eukrea_mbimx27-baseboard.c b/arch/arm/mach-mx2/eukrea_mbimx27-baseboard.c
new file mode 100644
index 00000000000..7382b6d27ee
--- /dev/null
+++ b/arch/arm/mach-mx2/eukrea_mbimx27-baseboard.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2009 Eric Benard - eric@eukrea.com
+ *
+ * Based on pcm970-baseboard.c which is :
+ * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+
+#include <asm/mach/arch.h>
+
+#include <mach/common.h>
+#include <mach/iomux.h>
+#include <mach/imxfb.h>
+#include <mach/hardware.h>
+#include <mach/mmc.h>
+#include <mach/imx-uart.h>
+
+#include "devices.h"
+
+static int eukrea_mbimx27_pins[] = {
+ /* UART2 */
+ PE3_PF_UART2_CTS,
+ PE4_PF_UART2_RTS,
+ PE6_PF_UART2_TXD,
+ PE7_PF_UART2_RXD,
+ /* UART3 */
+ PE8_PF_UART3_TXD,
+ PE9_PF_UART3_RXD,
+ PE10_PF_UART3_CTS,
+ PE11_PF_UART3_RTS,
+ /* UART4 */
+ PB26_AF_UART4_RTS,
+ PB28_AF_UART4_TXD,
+ PB29_AF_UART4_CTS,
+ PB31_AF_UART4_RXD,
+ /* SDHC1*/
+ PE18_PF_SD1_D0,
+ PE19_PF_SD1_D1,
+ PE20_PF_SD1_D2,
+ PE21_PF_SD1_D3,
+ PE22_PF_SD1_CMD,
+ PE23_PF_SD1_CLK,
+ /* display */
+ PA5_PF_LSCLK,
+ PA6_PF_LD0,
+ PA7_PF_LD1,
+ PA8_PF_LD2,
+ PA9_PF_LD3,
+ PA10_PF_LD4,
+ PA11_PF_LD5,
+ PA12_PF_LD6,
+ PA13_PF_LD7,
+ PA14_PF_LD8,
+ PA15_PF_LD9,
+ PA16_PF_LD10,
+ PA17_PF_LD11,
+ PA18_PF_LD12,
+ PA19_PF_LD13,
+ PA20_PF_LD14,
+ PA21_PF_LD15,
+ PA22_PF_LD16,
+ PA23_PF_LD17,
+ PA28_PF_HSYNC,
+ PA29_PF_VSYNC,
+ PA30_PF_CONTRAST,
+ PA31_PF_OE_ACD,
+ /* SPI1 */
+ PD28_PF_CSPI1_SS0,
+ PD29_PF_CSPI1_SCLK,
+ PD30_PF_CSPI1_MISO,
+ PD31_PF_CSPI1_MOSI,
+};
+
+static struct gpio_led gpio_leds[] = {
+ {
+ .name = "led1",
+ .default_trigger = "heartbeat",
+ .active_low = 1,
+ .gpio = GPIO_PORTF | 16,
+ },
+ {
+ .name = "led2",
+ .default_trigger = "none",
+ .active_low = 1,
+ .gpio = GPIO_PORTF | 19,
+ },
+ {
+ .name = "backlight",
+ .default_trigger = "backlight",
+ .active_low = 0,
+ .gpio = GPIO_PORTE | 5,
+ },
+};
+
+static struct gpio_led_platform_data gpio_led_info = {
+ .leds = gpio_leds,
+ .num_leds = ARRAY_SIZE(gpio_leds),
+};
+
+static struct platform_device leds_gpio = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &gpio_led_info,
+ },
+};
+
+static struct imx_fb_videomode eukrea_mbimx27_modes[] = {
+ {
+ .mode = {
+ .name = "CMO-QGVA",
+ .refresh = 60,
+ .xres = 320,
+ .yres = 240,
+ .pixclock = 156000,
+ .hsync_len = 30,
+ .left_margin = 38,
+ .right_margin = 20,
+ .vsync_len = 3,
+ .upper_margin = 15,
+ .lower_margin = 4,
+ },
+ .pcr = 0xFAD08B80,
+ .bpp = 16,
+ },
+};
+
+static struct imx_fb_platform_data eukrea_mbimx27_fb_data = {
+ .mode = eukrea_mbimx27_modes,
+ .num_modes = ARRAY_SIZE(eukrea_mbimx27_modes),
+
+ .pwmr = 0x00A903FF,
+ .lscr1 = 0x00120300,
+ .dmacr = 0x00040060,
+};
+
+static struct imxuart_platform_data uart_pdata[] = {
+ {
+ .flags = IMXUART_HAVE_RTSCTS,
+ },
+ {
+ .flags = IMXUART_HAVE_RTSCTS,
+ },
+};
+
+#if defined(CONFIG_TOUCHSCREEN_ADS7846)
+ || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
+
+#define ADS7846_PENDOWN (GPIO_PORTD | 25)
+
+static void ads7846_dev_init(void)
+{
+ if (gpio_request(ADS7846_PENDOWN, "ADS7846 pendown") < 0) {
+ printk(KERN_ERR "can't get ads746 pen down GPIO\n");
+ return;
+ }
+
+ gpio_direction_input(ADS7846_PENDOWN);
+}
+
+static int ads7846_get_pendown_state(void)
+{
+ return !gpio_get_value(ADS7846_PENDOWN);
+}
+
+static struct ads7846_platform_data ads7846_config __initdata = {
+ .get_pendown_state = ads7846_get_pendown_state,
+ .keep_vref_on = 1,
+};
+
+static struct spi_board_info eukrea_mbimx27_spi_board_info[] __initdata = {
+ [0] = {
+ .modalias = "ads7846",
+ .bus_num = 0,
+ .chip_select = 0,
+ .max_speed_hz = 1500000,
+ .irq = IRQ_GPIOD(25),
+ .platform_data = &ads7846_config,
+ .mode = SPI_MODE_2,
+ },
+};
+
+static int eukrea_mbimx27_spi_cs[] = {GPIO_PORTD | 28};
+
+static struct spi_imx_master eukrea_mbimx27_spi_0_data = {
+ .chipselect = eukrea_mbimx27_spi_cs,
+ .num_chipselect = ARRAY_SIZE(eukrea_mbimx27_spi_cs),
+};
+#endif
+
+static struct platform_device *platform_devices[] __initdata = {
+ &leds_gpio,
+};
+
+/*
+ * system init for baseboard usage. Will be called by cpuimx27 init.
+ *
+ * Add platform devices present on this baseboard and init
+ * them from CPU side as far as required to use them later on
+ */
+void __init eukrea_mbimx27_baseboard_init(void)
+{
+ mxc_gpio_setup_multiple_pins(eukrea_mbimx27_pins,
+ ARRAY_SIZE(eukrea_mbimx27_pins), "MBIMX27");
+
+ mxc_register_device(&mxc_uart_device1, &uart_pdata[0]);
+ mxc_register_device(&mxc_uart_device2, &uart_pdata[1]);
+
+ mxc_register_device(&mxc_fb_device, &eukrea_mbimx27_fb_data);
+ mxc_register_device(&mxc_sdhc_device0, NULL);
+
+#if defined(CONFIG_TOUCHSCREEN_ADS7846)
+ || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
+ /* SPI and ADS7846 Touchscreen controler init */
+ mxc_gpio_mode(GPIO_PORTD | 28 | GPIO_GPIO | GPIO_OUT);
+ mxc_gpio_mode(GPIO_PORTD | 25 | GPIO_GPIO | GPIO_IN);
+ mxc_register_device(&mxc_spi_device0, &eukrea_mbimx27_spi_0_data);
+ spi_register_board_info(eukrea_mbimx27_spi_board_info,
+ ARRAY_SIZE(eukrea_mbimx27_spi_board_info));
+ ads7846_dev_init();
+#endif
+
+ /* Leds configuration */
+ mxc_gpio_mode(GPIO_PORTF | 16 | GPIO_GPIO | GPIO_OUT);
+ mxc_gpio_mode(GPIO_PORTF | 19 | GPIO_GPIO | GPIO_OUT);
+ /* Backlight */
+ mxc_gpio_mode(GPIO_PORTE | 5 | GPIO_GPIO | GPIO_OUT);
+
+ platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));
+}
diff --git a/arch/arm/mach-mx2/generic.c b/arch/arm/mach-mx2/generic.c
index 169372f69d8..ae8f759134d 100644
--- a/arch/arm/mach-mx2/generic.c
+++ b/arch/arm/mach-mx2/generic.c
@@ -72,6 +72,7 @@ static struct map_desc mxc_io_desc[] __initdata = {
void __init mx21_map_io(void)
{
mxc_set_cpu_type(MXC_CPU_MX21);
+ mxc_arch_reset_init(IO_ADDRESS(WDOG_BASE_ADDR));
iotable_init(mxc_io_desc, ARRAY_SIZE(mxc_io_desc));
}
@@ -79,7 +80,18 @@ void __init mx21_map_io(void)
void __init mx27_map_io(void)
{
mxc_set_cpu_type(MXC_CPU_MX27);
+ mxc_arch_reset_init(IO_ADDRESS(WDOG_BASE_ADDR));
iotable_init(mxc_io_desc, ARRAY_SIZE(mxc_io_desc));
}
+void __init mx27_init_irq(void)
+{
+ mxc_init_irq(IO_ADDRESS(AVIC_BASE_ADDR));
+}
+
+void __init mx21_init_irq(void)
+{
+ mx27_init_irq();
+}
+
diff --git a/arch/arm/mach-mx2/mx21ads.c b/arch/arm/mach-mx2/mx21ads.c
index a5ee461cb40..cf5f77cbc2f 100644
--- a/arch/arm/mach-mx2/mx21ads.c
+++ b/arch/arm/mach-mx2/mx21ads.c
@@ -164,25 +164,33 @@ static void mx21ads_fb_exit(struct platform_device *pdev)
* Connected is a portrait Sharp-QVGA display
* of type: LQ035Q7DB02
*/
-static struct imx_fb_platform_data mx21ads_fb_data = {
- .pixclock = 188679, /* in ps */
- .xres = 240,
- .yres = 320,
-
- .bpp = 16,
- .hsync_len = 2,
- .left_margin = 6,
- .right_margin = 16,
+static struct imx_fb_videomode mx21ads_modes[] = {
+ {
+ .mode = {
+ .name = "Sharp-LQ035Q7",
+ .refresh = 60,
+ .xres = 240,
+ .yres = 320,
+ .pixclock = 188679, /* in ps (5.3MHz) */
+ .hsync_len = 2,
+ .left_margin = 6,
+ .right_margin = 16,
+ .vsync_len = 1,
+ .upper_margin = 8,
+ .lower_margin = 10,
+ },
+ .pcr = 0xfb108bc7,
+ .bpp = 16,
+ },
+};
- .vsync_len = 1,
- .upper_margin = 8,
- .lower_margin = 10,
- .fixed_screen_cpu = 0,
+static struct imx_fb_platform_data mx21ads_fb_data = {
+ .mode = mx21ads_modes,
+ .num_modes = ARRAY_SIZE(mx21ads_modes),
- .pcr = 0xFB108BC7,
- .pwmr = 0x00A901ff,
- .lscr1 = 0x00120300,
- .dmacr = 0x00020008,
+ .pwmr = 0x00a903ff,
+ .lscr1 = 0x00120300,
+ .dmacr = 0x00020008,
.init = mx21ads_fb_init,
.exit = mx21ads_fb_exit,
@@ -280,7 +288,7 @@ MACHINE_START(MX21ADS, "Freescale i.MX21ADS")
.io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx21ads_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx21_init_irq,
.init_machine = mx21ads_board_init,
.timer = &mx21ads_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx2/mx27ads.c b/arch/arm/mach-mx2/mx27ads.c
index 02daddac699..83e412b713e 100644
--- a/arch/arm/mach-mx2/mx27ads.c
+++ b/arch/arm/mach-mx2/mx27ads.c
@@ -183,20 +183,29 @@ void lcd_power(int on)
__raw_writew(PBC_BCTRL1_LCDON, PBC_BCTRL1_CLEAR_REG);
}
-static struct imx_fb_platform_data mx27ads_fb_data = {
- .pixclock = 188679,
- .xres = 240,
- .yres = 320,
-
- .bpp = 16,
- .hsync_len = 1,
- .left_margin = 9,
- .right_margin = 16,
+static struct imx_fb_videomode mx27ads_modes[] = {
+ {
+ .mode = {
+ .name = "Sharp-LQ035Q7",
+ .refresh = 60,
+ .xres = 240,
+ .yres = 320,
+ .pixclock = 188679, /* in ps (5.3MHz) */
+ .hsync_len = 1,
+ .left_margin = 9,
+ .right_margin = 16,
+ .vsync_len = 1,
+ .upper_margin = 7,
+ .lower_margin = 9,
+ },
+ .bpp = 16,
+ .pcr = 0xFB008BC0,
+ },
+};
- .vsync_len = 1,
- .upper_margin = 7,
- .lower_margin = 9,
- .fixed_screen_cpu = 0,
+static struct imx_fb_platform_data mx27ads_fb_data = {
+ .mode = mx27ads_modes,
+ .num_modes = ARRAY_SIZE(mx27ads_modes),
/*
* - HSYNC active high
@@ -207,7 +216,6 @@ static struct imx_fb_platform_data mx27ads_fb_data = {
* - data enable low active
* - enable sharp mode
*/
- .pcr = 0xFB008BC0,
.pwmr = 0x00A903FF,
.lscr1 = 0x00120300,
.dmacr = 0x00020010,
@@ -330,7 +338,7 @@ MACHINE_START(MX27ADS, "Freescale i.MX27ADS")
.io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx27ads_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx27_init_irq,
.init_machine = mx27ads_board_init,
.timer = &mx27ads_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx2/mx27lite.c b/arch/arm/mach-mx2/mx27lite.c
index 3ae11cb8c04..82ea227ea0c 100644
--- a/arch/arm/mach-mx2/mx27lite.c
+++ b/arch/arm/mach-mx2/mx27lite.c
@@ -89,7 +89,7 @@ MACHINE_START(IMX27LITE, "LogicPD i.MX27LITE")
.io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx27_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx27_init_irq,
.init_machine = mx27lite_init,
.timer = &mx27lite_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx2/mx27pdk.c b/arch/arm/mach-mx2/mx27pdk.c
index 1d9238c7a6c..6761d1b79e4 100644
--- a/arch/arm/mach-mx2/mx27pdk.c
+++ b/arch/arm/mach-mx2/mx27pdk.c
@@ -89,7 +89,7 @@ MACHINE_START(MX27_3DS, "Freescale MX27PDK")
.io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx27_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx27_init_irq,
.init_machine = mx27pdk_init,
.timer = &mx27pdk_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx2/pca100.c b/arch/arm/mach-mx2/pca100.c
new file mode 100644
index 00000000000..fe5b165b88c
--- /dev/null
+++ b/arch/arm/mach-mx2/pca100.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2007 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
+ * Copyright (C) 2009 Sascha Hauer (kernel@pengutronix.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/i2c/at24.h>
+#include <linux/dma-mapping.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/eeprom.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <mach/iomux.h>
+#include <mach/i2c.h>
+#include <asm/mach/time.h>
+#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE)
+#include <mach/spi.h>
+#endif
+#include <mach/imx-uart.h>
+#include <mach/mxc_nand.h>
+#include <mach/irqs.h>
+#include <mach/mmc.h>
+
+#include "devices.h"
+
+static int pca100_pins[] = {
+ /* UART1 */
+ PE12_PF_UART1_TXD,
+ PE13_PF_UART1_RXD,
+ PE14_PF_UART1_CTS,
+ PE15_PF_UART1_RTS,
+ /* SDHC */
+ PB4_PF_SD2_D0,
+ PB5_PF_SD2_D1,
+ PB6_PF_SD2_D2,
+ PB7_PF_SD2_D3,
+ PB8_PF_SD2_CMD,
+ PB9_PF_SD2_CLK,
+ /* FEC */
+ PD0_AIN_FEC_TXD0,
+ PD1_AIN_FEC_TXD1,
+ PD2_AIN_FEC_TXD2,
+ PD3_AIN_FEC_TXD3,
+ PD4_AOUT_FEC_RX_ER,
+ PD5_AOUT_FEC_RXD1,
+ PD6_AOUT_FEC_RXD2,
+ PD7_AOUT_FEC_RXD3,
+ PD8_AF_FEC_MDIO,
+ PD9_AIN_FEC_MDC,
+ PD10_AOUT_FEC_CRS,
+ PD11_AOUT_FEC_TX_CLK,
+ PD12_AOUT_FEC_RXD0,
+ PD13_AOUT_FEC_RX_DV,
+ PD14_AOUT_FEC_RX_CLK,
+ PD15_AOUT_FEC_COL,
+ PD16_AIN_FEC_TX_ER,
+ PF23_AIN_FEC_TX_EN,
+ /* SSI1 */
+ PC20_PF_SSI1_FS,
+ PC21_PF_SSI1_RXD,
+ PC22_PF_SSI1_TXD,
+ PC23_PF_SSI1_CLK,
+ /* onboard I2C */
+ PC5_PF_I2C2_SDA,
+ PC6_PF_I2C2_SCL,
+ /* external I2C */
+ PD17_PF_I2C_DATA,
+ PD18_PF_I2C_CLK,
+ /* SPI1 */
+ PD25_PF_CSPI1_RDY,
+ PD29_PF_CSPI1_SCLK,
+ PD30_PF_CSPI1_MISO,
+ PD31_PF_CSPI1_MOSI,
+};
+
+static struct imxuart_platform_data uart_pdata = {
+ .flags = IMXUART_HAVE_RTSCTS,
+};
+
+static struct mxc_nand_platform_data pca100_nand_board_info = {
+ .width = 1,
+ .hw_ecc = 1,
+};
+
+static struct platform_device *platform_devices[] __initdata = {
+ &mxc_w1_master_device,
+ &mxc_fec_device,
+};
+
+static struct imxi2c_platform_data pca100_i2c_1_data = {
+ .bitrate = 100000,
+};
+
+static struct at24_platform_data board_eeprom = {
+ .byte_len = 4096,
+ .page_size = 32,
+ .flags = AT24_FLAG_ADDR16,
+};
+
+static struct i2c_board_info pca100_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("at24", 0x52), /* E0=0, E1=1, E2=0 */
+ .platform_data = &board_eeprom,
+ }, {
+ I2C_BOARD_INFO("rtc-pcf8563", 0x51),
+ .type = "pcf8563"
+ }, {
+ I2C_BOARD_INFO("lm75", 0x4a),
+ .type = "lm75"
+ }
+};
+
+#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE)
+static struct spi_eeprom at25320 = {
+ .name = "at25320an",
+ .byte_len = 4096,
+ .page_size = 32,
+ .flags = EE_ADDR2,
+};
+
+static struct spi_board_info pca100_spi_board_info[] __initdata = {
+ {
+ .modalias = "at25",
+ .max_speed_hz = 30000,
+ .bus_num = 0,
+ .chip_select = 1,
+ .platform_data = &at25320,
+ },
+};
+
+static int pca100_spi_cs[] = {GPIO_PORTD + 28, GPIO_PORTD + 27};
+
+static struct spi_imx_master pca100_spi_0_data = {
+ .chipselect = pca100_spi_cs,
+ .num_chipselect = ARRAY_SIZE(pca100_spi_cs),
+};
+#endif
+
+static int pca100_sdhc2_init(struct device *dev, irq_handler_t detect_irq,
+ void *data)
+{
+ int ret;
+
+ ret = request_irq(IRQ_GPIOC(29), detect_irq,
+ IRQF_DISABLED | IRQF_TRIGGER_FALLING,
+ "imx-mmc-detect", data);
+ if (ret)
+ printk(KERN_ERR
+ "pca100: Failed to reuest irq for sd/mmc detection\n");
+
+ return ret;
+}
+
+static void pca100_sdhc2_exit(struct device *dev, void *data)
+{
+ free_irq(IRQ_GPIOC(29), data);
+}
+
+static struct imxmmc_platform_data sdhc_pdata = {
+ .init = pca100_sdhc2_init,
+ .exit = pca100_sdhc2_exit,
+};
+
+static void __init pca100_init(void)
+{
+ int ret;
+
+ ret = mxc_gpio_setup_multiple_pins(pca100_pins,
+ ARRAY_SIZE(pca100_pins), "PCA100");
+ if (ret)
+ printk(KERN_ERR "pca100: Failed to setup pins (%d)\n", ret);
+
+ mxc_register_device(&mxc_uart_device0, &uart_pdata);
+
+ mxc_gpio_mode(GPIO_PORTC | 29 | GPIO_GPIO | GPIO_IN);
+ mxc_register_device(&mxc_sdhc_device1, &sdhc_pdata);
+
+ mxc_register_device(&mxc_nand_device, &pca100_nand_board_info);
+
+ /* only the i2c master 1 is used on this CPU card */
+ i2c_register_board_info(1, pca100_i2c_devices,
+ ARRAY_SIZE(pca100_i2c_devices));
+
+ mxc_register_device(&mxc_i2c_device1, &pca100_i2c_1_data);
+
+ mxc_gpio_mode(GPIO_PORTD | 28 | GPIO_GPIO | GPIO_OUT);
+ mxc_gpio_mode(GPIO_PORTD | 27 | GPIO_GPIO | GPIO_OUT);
+
+ /* GPIO0_IRQ */
+ mxc_gpio_mode(GPIO_PORTC | 31 | GPIO_GPIO | GPIO_IN);
+ /* GPIO1_IRQ */
+ mxc_gpio_mode(GPIO_PORTC | 25 | GPIO_GPIO | GPIO_IN);
+ /* GPIO2_IRQ */
+ mxc_gpio_mode(GPIO_PORTE | 5 | GPIO_GPIO | GPIO_IN);
+
+#if defined(CONFIG_SPI_IMX) || defined(CONFIG_SPI_IMX_MODULE)
+ spi_register_board_info(pca100_spi_board_info,
+ ARRAY_SIZE(pca100_spi_board_info));
+ mxc_register_device(&mxc_spi_device0, &pca100_spi_0_data);
+#endif
+
+ platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));
+}
+
+static void __init pca100_timer_init(void)
+{
+ mx27_clocks_init(26000000);
+}
+
+static struct sys_timer pca100_timer = {
+ .init = pca100_timer_init,
+};
+
+MACHINE_START(PCA100, "phyCARD-i.MX27")
+ .phys_io = AIPI_BASE_ADDR,
+ .io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .map_io = mx27_map_io,
+ .init_irq = mxc_init_irq,
+ .init_machine = pca100_init,
+ .timer = &pca100_timer,
+MACHINE_END
+
diff --git a/arch/arm/mach-mx2/pcm038.c b/arch/arm/mach-mx2/pcm038.c
index a4628d00434..ee65dda584c 100644
--- a/arch/arm/mach-mx2/pcm038.c
+++ b/arch/arm/mach-mx2/pcm038.c
@@ -186,17 +186,13 @@ static struct at24_platform_data board_eeprom = {
};
static struct i2c_board_info pcm038_i2c_devices[] = {
- [0] = {
+ {
I2C_BOARD_INFO("at24", 0x52), /* E0=0, E1=1, E2=0 */
.platform_data = &board_eeprom,
- },
- [1] = {
- I2C_BOARD_INFO("rtc-pcf8563", 0x51),
- .type = "pcf8563"
- },
- [2] = {
+ }, {
+ I2C_BOARD_INFO("pcf8563", 0x51),
+ }, {
I2C_BOARD_INFO("lm75", 0x4a),
- .type = "lm75"
}
};
@@ -220,6 +216,9 @@ static void __init pcm038_init(void)
mxc_register_device(&mxc_i2c_device1, &pcm038_i2c_1_data);
+ /* PE18 for user-LED D40 */
+ mxc_gpio_mode(GPIO_PORTE | 18 | GPIO_GPIO | GPIO_OUT);
+
platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices));
#ifdef CONFIG_MACH_PCM970_BASEBOARD
@@ -241,7 +240,7 @@ MACHINE_START(PCM038, "phyCORE-i.MX27")
.io_pg_offst = ((AIPI_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx27_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx27_init_irq,
.init_machine = pcm038_init,
.timer = &pcm038_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx2/pcm970-baseboard.c b/arch/arm/mach-mx2/pcm970-baseboard.c
index 6a3acaf57dd..c261f59b0b4 100644
--- a/arch/arm/mach-mx2/pcm970-baseboard.c
+++ b/arch/arm/mach-mx2/pcm970-baseboard.c
@@ -19,6 +19,7 @@
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
+#include <linux/can/platform/sja1000.h>
#include <asm/mach/arch.h>
@@ -125,40 +126,96 @@ static struct imxmmc_platform_data sdhc_pdata = {
.exit = pcm970_sdhc2_exit,
};
-/*
- * Connected is a portrait Sharp-QVGA display
- * of type: LQ035Q7DH06
- */
-static struct imx_fb_platform_data pcm038_fb_data = {
- .pixclock = 188679, /* in ps (5.3MHz) */
- .xres = 240,
- .yres = 320,
-
- .bpp = 16,
- .hsync_len = 7,
- .left_margin = 5,
- .right_margin = 16,
+static struct imx_fb_videomode pcm970_modes[] = {
+ {
+ .mode = {
+ .name = "Sharp-LQ035Q7",
+ .refresh = 60,
+ .xres = 240,
+ .yres = 320,
+ .pixclock = 188679, /* in ps (5.3MHz) */
+ .hsync_len = 7,
+ .left_margin = 5,
+ .right_margin = 16,
+ .vsync_len = 1,
+ .upper_margin = 7,
+ .lower_margin = 9,
+ },
+ /*
+ * - HSYNC active high
+ * - VSYNC active high
+ * - clk notenabled while idle
+ * - clock not inverted
+ * - data not inverted
+ * - data enable low active
+ * - enable sharp mode
+ */
+ .pcr = 0xF00080C0,
+ .bpp = 16,
+ }, {
+ .mode = {
+ .name = "TX090",
+ .refresh = 60,
+ .xres = 240,
+ .yres = 320,
+ .pixclock = 38255,
+ .left_margin = 144,
+ .right_margin = 0,
+ .upper_margin = 7,
+ .lower_margin = 40,
+ .hsync_len = 96,
+ .vsync_len = 1,
+ },
+ /*
+ * - HSYNC active low (1 << 22)
+ * - VSYNC active low (1 << 23)
+ * - clk notenabled while idle
+ * - clock not inverted
+ * - data not inverted
+ * - data enable low active
+ * - enable sharp mode
+ */
+ .pcr = 0xF0008080 | (1<<22) | (1<<23) | (1<<19),
+ .bpp = 32,
+ },
+};
- .vsync_len = 1,
- .upper_margin = 7,
- .lower_margin = 9,
- .fixed_screen_cpu = 0,
+static struct imx_fb_platform_data pcm038_fb_data = {
+ .mode = pcm970_modes,
+ .num_modes = ARRAY_SIZE(pcm970_modes),
- /*
- * - HSYNC active high
- * - VSYNC active high
- * - clk notenabled while idle
- * - clock not inverted
- * - data not inverted
- * - data enable low active
- * - enable sharp mode
- */
- .pcr = 0xFA0080C0,
.pwmr = 0x00A903FF,
.lscr1 = 0x00120300,
.dmacr = 0x00020010,
};
+static struct resource pcm970_sja1000_resources[] = {
+ {
+ .start = CS4_BASE_ADDR,
+ .end = CS4_BASE_ADDR + 0x100 - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IRQ_GPIOE(19),
+ .end = IRQ_GPIOE(19),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE,
+ },
+};
+
+struct sja1000_platform_data pcm970_sja1000_platform_data = {
+ .clock = 16000000 / 2,
+ .ocr = 0x40 | 0x18,
+ .cdr = 0x40,
+};
+
+static struct platform_device pcm970_sja1000 = {
+ .name = "sja1000_platform",
+ .dev = {
+ .platform_data = &pcm970_sja1000_platform_data,
+ },
+ .resource = pcm970_sja1000_resources,
+ .num_resources = ARRAY_SIZE(pcm970_sja1000_resources),
+};
+
/*
* system init for baseboard usage. Will be called by pcm038 init.
*
@@ -172,4 +229,5 @@ void __init pcm970_baseboard_init(void)
mxc_register_device(&mxc_fb_device, &pcm038_fb_data);
mxc_register_device(&mxc_sdhc_device1, &sdhc_pdata);
+ platform_device_register(&pcm970_sja1000);
}
diff --git a/arch/arm/mach-mx25/Kconfig b/arch/arm/mach-mx25/Kconfig
new file mode 100644
index 00000000000..cc28f56eae8
--- /dev/null
+++ b/arch/arm/mach-mx25/Kconfig
@@ -0,0 +1,9 @@
+if ARCH_MX25
+
+comment "MX25 platforms:"
+
+config MACH_MX25_3DS
+ select ARCH_MXC_IOMUX_V3
+ bool "Support MX25PDK (3DS) Platform"
+
+endif
diff --git a/arch/arm/mach-mx25/Makefile b/arch/arm/mach-mx25/Makefile
new file mode 100644
index 00000000000..fe23836a9f3
--- /dev/null
+++ b/arch/arm/mach-mx25/Makefile
@@ -0,0 +1,3 @@
+obj-y := mm.o devices.o
+obj-$(CONFIG_ARCH_MX25) += clock.o
+obj-$(CONFIG_MACH_MX25_3DS) += mx25pdk.o
diff --git a/arch/arm/mach-mx25/Makefile.boot b/arch/arm/mach-mx25/Makefile.boot
new file mode 100644
index 00000000000..e1dd366f836
--- /dev/null
+++ b/arch/arm/mach-mx25/Makefile.boot
@@ -0,0 +1,3 @@
+ zreladdr-y := 0x80008000
+params_phys-y := 0x80000100
+initrd_phys-y := 0x80800000
diff --git a/arch/arm/mach-mx25/clock.c b/arch/arm/mach-mx25/clock.c
new file mode 100644
index 00000000000..ef26951a527
--- /dev/null
+++ b/arch/arm/mach-mx25/clock.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2009 by Sascha Hauer, Pengutronix
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <asm/clkdev.h>
+
+#include <mach/clock.h>
+#include <mach/hardware.h>
+#include <mach/common.h>
+#include <mach/mx25.h>
+
+#define CRM_BASE MX25_IO_ADDRESS(MX25_CRM_BASE_ADDR)
+
+#define CCM_MPCTL 0x00
+#define CCM_UPCTL 0x04
+#define CCM_CCTL 0x08
+#define CCM_CGCR0 0x0C
+#define CCM_CGCR1 0x10
+#define CCM_CGCR2 0x14
+#define CCM_PCDR0 0x18
+#define CCM_PCDR1 0x1C
+#define CCM_PCDR2 0x20
+#define CCM_PCDR3 0x24
+#define CCM_RCSR 0x28
+#define CCM_CRDR 0x2C
+#define CCM_DCVR0 0x30
+#define CCM_DCVR1 0x34
+#define CCM_DCVR2 0x38
+#define CCM_DCVR3 0x3c
+#define CCM_LTR0 0x40
+#define CCM_LTR1 0x44
+#define CCM_LTR2 0x48
+#define CCM_LTR3 0x4c
+
+static unsigned long get_rate_mpll(void)
+{
+ ulong mpctl = __raw_readl(CRM_BASE + CCM_MPCTL);
+
+ return mxc_decode_pll(mpctl, 24000000);
+}
+
+static unsigned long get_rate_upll(void)
+{
+ ulong mpctl = __raw_readl(CRM_BASE + CCM_UPCTL);
+
+ return mxc_decode_pll(mpctl, 24000000);
+}
+
+unsigned long get_rate_arm(struct clk *clk)
+{
+ unsigned long cctl = readl(CRM_BASE + CCM_CCTL);
+ unsigned long rate = get_rate_mpll();
+
+ if (cctl & (1 << 14))
+ rate = (rate * 3) >> 1;
+
+ return rate / ((cctl >> 30) + 1);
+}
+
+static unsigned long get_rate_ahb(struct clk *clk)
+{
+ unsigned long cctl = readl(CRM_BASE + CCM_CCTL);
+
+ return get_rate_arm(NULL) / (((cctl >> 28) & 0x3) + 1);
+}
+
+static unsigned long get_rate_ipg(struct clk *clk)
+{
+ return get_rate_ahb(NULL) >> 1;
+}
+
+static unsigned long get_rate_per(int per)
+{
+ unsigned long ofs = (per & 0x3) * 8;
+ unsigned long reg = per & ~0x3;
+ unsigned long val = (readl(CRM_BASE + CCM_PCDR0 + reg) >> ofs) & 0x3f;
+ unsigned long fref;
+
+ if (readl(CRM_BASE + 0x64) & (1 << per))
+ fref = get_rate_upll();
+ else
+ fref = get_rate_ipg(NULL);
+
+ return fref / (val + 1);
+}
+
+static unsigned long get_rate_uart(struct clk *clk)
+{
+ return get_rate_per(15);
+}
+
+static unsigned long get_rate_i2c(struct clk *clk)
+{
+ return get_rate_per(6);
+}
+
+static unsigned long get_rate_nfc(struct clk *clk)
+{
+ return get_rate_per(8);
+}
+
+static unsigned long get_rate_otg(struct clk *clk)
+{
+ return 48000000; /* FIXME */
+}
+
+static int clk_cgcr_enable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg |= 1 << clk->enable_shift;
+ __raw_writel(reg, clk->enable_reg);
+
+ return 0;
+}
+
+static void clk_cgcr_disable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(1 << clk->enable_shift);
+ __raw_writel(reg, clk->enable_reg);
+}
+
+#define DEFINE_CLOCK(name, i, er, es, gr, sr) \
+ static struct clk name = { \
+ .id = i, \
+ .enable_reg = CRM_BASE + er, \
+ .enable_shift = es, \
+ .get_rate = gr, \
+ .set_rate = sr, \
+ .enable = clk_cgcr_enable, \
+ .disable = clk_cgcr_disable, \
+ }
+
+DEFINE_CLOCK(gpt_clk, 0, CCM_CGCR0, 5, get_rate_ipg, NULL);
+DEFINE_CLOCK(cspi1_clk, 0, CCM_CGCR1, 5, get_rate_ipg, NULL);
+DEFINE_CLOCK(cspi2_clk, 0, CCM_CGCR1, 6, get_rate_ipg, NULL);
+DEFINE_CLOCK(cspi3_clk, 0, CCM_CGCR1, 7, get_rate_ipg, NULL);
+DEFINE_CLOCK(uart1_clk, 0, CCM_CGCR2, 14, get_rate_uart, NULL);
+DEFINE_CLOCK(uart2_clk, 0, CCM_CGCR2, 15, get_rate_uart, NULL);
+DEFINE_CLOCK(uart3_clk, 0, CCM_CGCR2, 16, get_rate_uart, NULL);
+DEFINE_CLOCK(uart4_clk, 0, CCM_CGCR2, 17, get_rate_uart, NULL);
+DEFINE_CLOCK(uart5_clk, 0, CCM_CGCR2, 18, get_rate_uart, NULL);
+DEFINE_CLOCK(nfc_clk, 0, CCM_CGCR0, 8, get_rate_nfc, NULL);
+DEFINE_CLOCK(usbotg_clk, 0, CCM_CGCR0, 28, get_rate_otg, NULL);
+DEFINE_CLOCK(pwm1_clk, 0, CCM_CGCR1, 31, get_rate_ipg, NULL);
+DEFINE_CLOCK(pwm2_clk, 0, CCM_CGCR2, 0, get_rate_ipg, NULL);
+DEFINE_CLOCK(pwm3_clk, 0, CCM_CGCR2, 1, get_rate_ipg, NULL);
+DEFINE_CLOCK(pwm4_clk, 0, CCM_CGCR2, 2, get_rate_ipg, NULL);
+DEFINE_CLOCK(kpp_clk, 0, CCM_CGCR1, 28, get_rate_ipg, NULL);
+DEFINE_CLOCK(tsc_clk, 0, CCM_CGCR2, 13, get_rate_ipg, NULL);
+DEFINE_CLOCK(i2c_clk, 0, CCM_CGCR0, 6, get_rate_i2c, NULL);
+
+#define _REGISTER_CLOCK(d, n, c) \
+ { \
+ .dev_id = d, \
+ .con_id = n, \
+ .clk = &c, \
+ },
+
+static struct clk_lookup lookups[] = {
+ _REGISTER_CLOCK("imx-uart.0", NULL, uart1_clk)
+ _REGISTER_CLOCK("imx-uart.1", NULL, uart2_clk)
+ _REGISTER_CLOCK("imx-uart.2", NULL, uart3_clk)
+ _REGISTER_CLOCK("imx-uart.3", NULL, uart4_clk)
+ _REGISTER_CLOCK("imx-uart.4", NULL, uart5_clk)
+ _REGISTER_CLOCK("mxc-ehci.0", "usb", usbotg_clk)
+ _REGISTER_CLOCK("mxc-ehci.1", "usb", usbotg_clk)
+ _REGISTER_CLOCK("mxc-ehci.2", "usb", usbotg_clk)
+ _REGISTER_CLOCK("fsl-usb2-udc", "usb", usbotg_clk)
+ _REGISTER_CLOCK("mxc_nand.0", NULL, nfc_clk)
+ _REGISTER_CLOCK("spi_imx.0", NULL, cspi1_clk)
+ _REGISTER_CLOCK("spi_imx.1", NULL, cspi2_clk)
+ _REGISTER_CLOCK("spi_imx.2", NULL, cspi3_clk)
+ _REGISTER_CLOCK("mxc_pwm.0", NULL, pwm1_clk)
+ _REGISTER_CLOCK("mxc_pwm.1", NULL, pwm2_clk)
+ _REGISTER_CLOCK("mxc_pwm.2", NULL, pwm3_clk)
+ _REGISTER_CLOCK("mxc_pwm.3", NULL, pwm4_clk)
+ _REGISTER_CLOCK("mxc-keypad", NULL, kpp_clk)
+ _REGISTER_CLOCK("mx25-adc", NULL, tsc_clk)
+ _REGISTER_CLOCK("imx-i2c.0", NULL, i2c_clk)
+ _REGISTER_CLOCK("imx-i2c.1", NULL, i2c_clk)
+ _REGISTER_CLOCK("imx-i2c.2", NULL, i2c_clk)
+};
+
+int __init mx25_clocks_init(unsigned long fref)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lookups); i++)
+ clkdev_add(&lookups[i]);
+
+ mxc_timer_init(&gpt_clk, MX25_IO_ADDRESS(MX25_GPT1_BASE_ADDR), 54);
+
+ return 0;
+}
diff --git a/arch/arm/mach-mx25/devices.c b/arch/arm/mach-mx25/devices.c
new file mode 100644
index 00000000000..eb12de1da42
--- /dev/null
+++ b/arch/arm/mach-mx25/devices.c
@@ -0,0 +1,402 @@
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <mach/mx25.h>
+#include <mach/irqs.h>
+
+static struct resource uart0[] = {
+ {
+ .start = 0x43f90000,
+ .end = 0x43f93fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 45,
+ .end = 45,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_uart_device0 = {
+ .name = "imx-uart",
+ .id = 0,
+ .resource = uart0,
+ .num_resources = ARRAY_SIZE(uart0),
+};
+
+static struct resource uart1[] = {
+ {
+ .start = 0x43f94000,
+ .end = 0x43f97fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 32,
+ .end = 32,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_uart_device1 = {
+ .name = "imx-uart",
+ .id = 1,
+ .resource = uart1,
+ .num_resources = ARRAY_SIZE(uart1),
+};
+
+static struct resource uart2[] = {
+ {
+ .start = 0x5000c000,
+ .end = 0x5000ffff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 18,
+ .end = 18,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_uart_device2 = {
+ .name = "imx-uart",
+ .id = 2,
+ .resource = uart2,
+ .num_resources = ARRAY_SIZE(uart2),
+};
+
+static struct resource uart3[] = {
+ {
+ .start = 0x50008000,
+ .end = 0x5000bfff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 5,
+ .end = 5,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_uart_device3 = {
+ .name = "imx-uart",
+ .id = 3,
+ .resource = uart3,
+ .num_resources = ARRAY_SIZE(uart3),
+};
+
+static struct resource uart4[] = {
+ {
+ .start = 0x5002c000,
+ .end = 0x5002ffff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 40,
+ .end = 40,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_uart_device4 = {
+ .name = "imx-uart",
+ .id = 4,
+ .resource = uart4,
+ .num_resources = ARRAY_SIZE(uart4),
+};
+
+#define MX25_OTG_BASE_ADDR 0x53FF4000
+
+static u64 otg_dmamask = DMA_BIT_MASK(32);
+
+static struct resource mxc_otg_resources[] = {
+ {
+ .start = MX25_OTG_BASE_ADDR,
+ .end = MX25_OTG_BASE_ADDR + 0x1ff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 37,
+ .end = 37,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_otg = {
+ .name = "mxc-ehci",
+ .id = 0,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ .dma_mask = &otg_dmamask,
+ },
+ .resource = mxc_otg_resources,
+ .num_resources = ARRAY_SIZE(mxc_otg_resources),
+};
+
+/* OTG gadget device */
+struct platform_device otg_udc_device = {
+ .name = "fsl-usb2-udc",
+ .id = -1,
+ .dev = {
+ .dma_mask = &otg_dmamask,
+ .coherent_dma_mask = 0xffffffff,
+ },
+ .resource = mxc_otg_resources,
+ .num_resources = ARRAY_SIZE(mxc_otg_resources),
+};
+
+static u64 usbh2_dmamask = DMA_BIT_MASK(32);
+
+static struct resource mxc_usbh2_resources[] = {
+ {
+ .start = MX25_OTG_BASE_ADDR + 0x400,
+ .end = MX25_OTG_BASE_ADDR + 0x5ff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 35,
+ .end = 35,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_usbh2 = {
+ .name = "mxc-ehci",
+ .id = 1,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ .dma_mask = &usbh2_dmamask,
+ },
+ .resource = mxc_usbh2_resources,
+ .num_resources = ARRAY_SIZE(mxc_usbh2_resources),
+};
+
+static struct resource mxc_spi_resources0[] = {
+ {
+ .start = 0x43fa4000,
+ .end = 0x43fa7fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 14,
+ .end = 14,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_spi_device0 = {
+ .name = "spi_imx",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_spi_resources0),
+ .resource = mxc_spi_resources0,
+};
+
+static struct resource mxc_spi_resources1[] = {
+ {
+ .start = 0x50010000,
+ .end = 0x50013fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 13,
+ .end = 13,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_spi_device1 = {
+ .name = "spi_imx",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(mxc_spi_resources1),
+ .resource = mxc_spi_resources1,
+};
+
+static struct resource mxc_spi_resources2[] = {
+ {
+ .start = 0x50004000,
+ .end = 0x50007fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_spi_device2 = {
+ .name = "spi_imx",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(mxc_spi_resources2),
+ .resource = mxc_spi_resources2,
+};
+
+static struct resource mxc_pwm_resources0[] = {
+ {
+ .start = 0x53fe0000,
+ .end = 0x53fe3fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 26,
+ .end = 26,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device mxc_pwm_device0 = {
+ .name = "mxc_pwm",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_pwm_resources0),
+ .resource = mxc_pwm_resources0,
+};
+
+static struct resource mxc_pwm_resources1[] = {
+ {
+ .start = 0x53fa0000,
+ .end = 0x53fa3fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 36,
+ .end = 36,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device mxc_pwm_device1 = {
+ .name = "mxc_pwm",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(mxc_pwm_resources1),
+ .resource = mxc_pwm_resources1,
+};
+
+static struct resource mxc_pwm_resources2[] = {
+ {
+ .start = 0x53fa8000,
+ .end = 0x53fabfff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 41,
+ .end = 41,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device mxc_pwm_device2 = {
+ .name = "mxc_pwm",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(mxc_pwm_resources2),
+ .resource = mxc_pwm_resources2,
+};
+
+static struct resource mxc_keypad_resources[] = {
+ {
+ .start = 0x43fa8000,
+ .end = 0x43fabfff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 24,
+ .end = 24,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device mxc_keypad_device = {
+ .name = "mxc-keypad",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(mxc_keypad_resources),
+ .resource = mxc_keypad_resources,
+};
+
+static struct resource mxc_pwm_resources3[] = {
+ {
+ .start = 0x53fc8000,
+ .end = 0x53fcbfff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 42,
+ .end = 42,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device mxc_pwm_device3 = {
+ .name = "mxc_pwm",
+ .id = 3,
+ .num_resources = ARRAY_SIZE(mxc_pwm_resources3),
+ .resource = mxc_pwm_resources3,
+};
+
+static struct resource mxc_i2c_1_resources[] = {
+ {
+ .start = 0x43f80000,
+ .end = 0x43f83fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 3,
+ .end = 3,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device mxc_i2c_device0 = {
+ .name = "imx-i2c",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_i2c_1_resources),
+ .resource = mxc_i2c_1_resources,
+};
+
+static struct resource mxc_i2c_2_resources[] = {
+ {
+ .start = 0x43f98000,
+ .end = 0x43f9bfff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 4,
+ .end = 4,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device mxc_i2c_device1 = {
+ .name = "imx-i2c",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(mxc_i2c_2_resources),
+ .resource = mxc_i2c_2_resources,
+};
+
+static struct resource mxc_i2c_3_resources[] = {
+ {
+ .start = 0x43f84000,
+ .end = 0x43f87fff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = 10,
+ .end = 10,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device mxc_i2c_device2 = {
+ .name = "imx-i2c",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(mxc_i2c_3_resources),
+ .resource = mxc_i2c_3_resources,
+};
+
+static struct mxc_gpio_port imx_gpio_ports[] = {
+ {
+ .chip.label = "gpio-0",
+ .base = (void __iomem *)MX25_GPIO1_BASE_ADDR_VIRT,
+ .irq = 52,
+ .virtual_irq_start = MXC_GPIO_IRQ_START,
+ }, {
+ .chip.label = "gpio-1",
+ .base = (void __iomem *)MX25_GPIO2_BASE_ADDR_VIRT,
+ .irq = 51,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 32,
+ }, {
+ .chip.label = "gpio-2",
+ .base = (void __iomem *)MX25_GPIO3_BASE_ADDR_VIRT,
+ .irq = 16,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 64,
+ }, {
+ .chip.label = "gpio-3",
+ .base = (void __iomem *)MX25_GPIO4_BASE_ADDR_VIRT,
+ .irq = 23,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 96,
+ }
+};
+
+int __init mxc_register_gpios(void)
+{
+ return mxc_gpio_init(imx_gpio_ports, ARRAY_SIZE(imx_gpio_ports));
+}
+
diff --git a/arch/arm/mach-mx25/devices.h b/arch/arm/mach-mx25/devices.h
new file mode 100644
index 00000000000..fe6bf88ad1d
--- /dev/null
+++ b/arch/arm/mach-mx25/devices.h
@@ -0,0 +1,19 @@
+extern struct platform_device mxc_uart_device0;
+extern struct platform_device mxc_uart_device1;
+extern struct platform_device mxc_uart_device2;
+extern struct platform_device mxc_uart_device3;
+extern struct platform_device mxc_uart_device4;
+extern struct platform_device mxc_otg;
+extern struct platform_device otg_udc_device;
+extern struct platform_device mxc_usbh2;
+extern struct platform_device mxc_spi_device0;
+extern struct platform_device mxc_spi_device1;
+extern struct platform_device mxc_spi_device2;
+extern struct platform_device mxc_pwm_device0;
+extern struct platform_device mxc_pwm_device1;
+extern struct platform_device mxc_pwm_device2;
+extern struct platform_device mxc_pwm_device3;
+extern struct platform_device mxc_keypad_device;
+extern struct platform_device mxc_i2c_device0;
+extern struct platform_device mxc_i2c_device1;
+extern struct platform_device mxc_i2c_device2;
diff --git a/arch/arm/mach-mx25/mm.c b/arch/arm/mach-mx25/mm.c
new file mode 100644
index 00000000000..a7e587ff3e9
--- /dev/null
+++ b/arch/arm/mach-mx25/mm.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 1999,2000 Arm Limited
+ * Copyright (C) 2000 Deep Blue Solutions Ltd
+ * Copyright (C) 2002 Shane Nay (shane@minirl.com)
+ * Copyright 2005-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * - add MX31 specific definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/err.h>
+
+#include <asm/pgtable.h>
+#include <asm/mach/map.h>
+
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <mach/mx25.h>
+#include <mach/iomux-v3.h>
+
+/*
+ * This table defines static virtual address mappings for I/O regions.
+ * These are the mappings common across all MX3 boards.
+ */
+static struct map_desc mxc_io_desc[] __initdata = {
+ {
+ .virtual = MX25_AVIC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(MX25_AVIC_BASE_ADDR),
+ .length = MX25_AVIC_SIZE,
+ .type = MT_DEVICE_NONSHARED
+ }, {
+ .virtual = MX25_AIPS1_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(MX25_AIPS1_BASE_ADDR),
+ .length = MX25_AIPS1_SIZE,
+ .type = MT_DEVICE_NONSHARED
+ }, {
+ .virtual = MX25_AIPS2_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(MX25_AIPS2_BASE_ADDR),
+ .length = MX25_AIPS2_SIZE,
+ .type = MT_DEVICE_NONSHARED
+ },
+};
+
+/*
+ * This function initializes the memory map. It is called during the
+ * system startup to create static physical to virtual memory mappings
+ * for the IO modules.
+ */
+void __init mx25_map_io(void)
+{
+ mxc_set_cpu_type(MXC_CPU_MX25);
+ mxc_iomux_v3_init(MX25_IO_ADDRESS(MX25_IOMUXC_BASE_ADDR));
+ mxc_arch_reset_init(MX25_IO_ADDRESS(MX25_WDOG_BASE_ADDR));
+
+ iotable_init(mxc_io_desc, ARRAY_SIZE(mxc_io_desc));
+}
+
+void __init mx25_init_irq(void)
+{
+ mxc_init_irq((void __iomem *)MX25_AVIC_BASE_ADDR_VIRT);
+}
+
diff --git a/arch/arm/mach-mx25/mx25pdk.c b/arch/arm/mach-mx25/mx25pdk.c
new file mode 100644
index 00000000000..92aa4fd19d9
--- /dev/null
+++ b/arch/arm/mach-mx25/mx25pdk.c
@@ -0,0 +1,58 @@
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/smsc911x.h>
+#include <linux/platform_device.h>
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <asm/memory.h>
+#include <asm/mach/map.h>
+#include <mach/common.h>
+#include <mach/imx-uart.h>
+#include <mach/mx25.h>
+#include <mach/mxc_nand.h>
+#include "devices.h"
+#include <mach/iomux-v3.h>
+
+static struct imxuart_platform_data uart_pdata = {
+ .flags = IMXUART_HAVE_RTSCTS,
+};
+
+static struct mxc_nand_platform_data nand_board_info = {
+ .width = 1,
+ .hw_ecc = 1,
+};
+
+static void __init mx25pdk_init(void)
+{
+ mxc_register_device(&mxc_uart_device0, &uart_pdata);
+ mxc_register_device(&mxc_usbh2, NULL);
+ mxc_register_device(&mxc_nand_device, &nand_board_info);
+}
+
+
+static void __init mx25pdk_timer_init(void)
+{
+ mx25_clocks_init(26000000);
+}
+
+static struct sys_timer mx25pdk_timer = {
+ .init = mx25pdk_timer_init,
+};
+
+MACHINE_START(MX25_3DS, "Freescale MX25PDK (3DS)")
+ /* Maintainer: Freescale Semiconductor, Inc. */
+ .phys_io = MX25_AIPS1_BASE_ADDR,
+ .io_pg_offst = ((MX25_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .map_io = mx25_map_io,
+ .init_irq = mx25_init_irq,
+ .init_machine = mx25pdk_init,
+ .timer = &mx25pdk_timer,
+MACHINE_END
+
diff --git a/arch/arm/mach-mx3/armadillo5x0.c b/arch/arm/mach-mx3/armadillo5x0.c
index ee331fd6b1b..776c0ee1b3c 100644
--- a/arch/arm/mach-mx3/armadillo5x0.c
+++ b/arch/arm/mach-mx3/armadillo5x0.c
@@ -352,7 +352,7 @@ MACHINE_START(ARMADILLO5X0, "Armadillo-500")
.io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x00000100,
.map_io = mx31_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx31_init_irq,
.timer = &armadillo5x0_timer,
.init_machine = armadillo5x0_init,
MACHINE_END
diff --git a/arch/arm/mach-mx3/clock-imx35.c b/arch/arm/mach-mx3/clock-imx35.c
index 577ee83d1f6..fe5c4217322 100644
--- a/arch/arm/mach-mx3/clock-imx35.c
+++ b/arch/arm/mach-mx3/clock-imx35.c
@@ -273,6 +273,19 @@ static unsigned long get_rate_csi(struct clk *clk)
return rate / get_3_3_div((pdr2 >> 16) & 0x3f);
}
+static unsigned long get_rate_otg(struct clk *clk)
+{
+ unsigned long pdr4 = __raw_readl(CCM_BASE + CCM_PDR4);
+ unsigned long rate;
+
+ if (pdr4 & (1 << 9))
+ rate = get_rate_arm();
+ else
+ rate = get_rate_ppll();
+
+ return rate / get_3_3_div((pdr4 >> 22) & 0x3f);
+}
+
static unsigned long get_rate_ipg_per(struct clk *clk)
{
unsigned long pdr0 = __raw_readl(CCM_BASE + CCM_PDR0);
@@ -365,7 +378,7 @@ DEFINE_CLOCK(ssi2_clk, 1, CCM_CGR2, 14, get_rate_ssi, NULL);
DEFINE_CLOCK(uart1_clk, 0, CCM_CGR2, 16, get_rate_uart, NULL);
DEFINE_CLOCK(uart2_clk, 1, CCM_CGR2, 18, get_rate_uart, NULL);
DEFINE_CLOCK(uart3_clk, 2, CCM_CGR2, 20, get_rate_uart, NULL);
-DEFINE_CLOCK(usbotg_clk, 0, CCM_CGR2, 22, NULL, NULL);
+DEFINE_CLOCK(usbotg_clk, 0, CCM_CGR2, 22, get_rate_otg, NULL);
DEFINE_CLOCK(wdog_clk, 0, CCM_CGR2, 24, NULL, NULL);
DEFINE_CLOCK(max_clk, 0, CCM_CGR2, 26, NULL, NULL);
DEFINE_CLOCK(admux_clk, 0, CCM_CGR2, 30, NULL, NULL);
@@ -426,7 +439,10 @@ static struct clk_lookup lookups[] = {
_REGISTER_CLOCK("imx-uart.0", NULL, uart1_clk)
_REGISTER_CLOCK("imx-uart.1", NULL, uart2_clk)
_REGISTER_CLOCK("imx-uart.2", NULL, uart3_clk)
- _REGISTER_CLOCK(NULL, "usbotg", usbotg_clk)
+ _REGISTER_CLOCK("mxc-ehci.0", "usb", usbotg_clk)
+ _REGISTER_CLOCK("mxc-ehci.1", "usb", usbotg_clk)
+ _REGISTER_CLOCK("mxc-ehci.2", "usb", usbotg_clk)
+ _REGISTER_CLOCK("fsl-usb2-udc", "usb", usbotg_clk)
_REGISTER_CLOCK("mxc_wdt.0", NULL, wdog_clk)
_REGISTER_CLOCK(NULL, "max", max_clk)
_REGISTER_CLOCK(NULL, "admux", admux_clk)
@@ -456,7 +472,7 @@ int __init mx35_clocks_init()
__raw_writel((3 << 26) | ll, CCM_BASE + CCM_CGR2);
__raw_writel(0, CCM_BASE + CCM_CGR3);
- mxc_timer_init(&gpt_clk);
+ mxc_timer_init(&gpt_clk, IO_ADDRESS(GPT1_BASE_ADDR), MXC_INT_GPT);
return 0;
}
diff --git a/arch/arm/mach-mx3/clock.c b/arch/arm/mach-mx3/clock.c
index 8b14239724c..06bd6180bfc 100644
--- a/arch/arm/mach-mx3/clock.c
+++ b/arch/arm/mach-mx3/clock.c
@@ -29,6 +29,7 @@
#include <mach/clock.h>
#include <mach/hardware.h>
+#include <mach/mx31.h>
#include <mach/common.h>
#include "crm_regs.h"
@@ -402,6 +403,11 @@ static unsigned long clk_ckih_get_rate(struct clk *clk)
return ckih_rate;
}
+static unsigned long clk_ckil_get_rate(struct clk *clk)
+{
+ return CKIL_CLK_FREQ;
+}
+
static struct clk ckih_clk = {
.get_rate = clk_ckih_get_rate,
};
@@ -508,6 +514,7 @@ DEFINE_CLOCK(usb_clk1, 0, NULL, 0, usb_get_rate, NULL, &usb_pll_clk)
DEFINE_CLOCK(nfc_clk, 0, NULL, 0, nfc_get_rate, NULL, &ahb_clk);
DEFINE_CLOCK(scc_clk, 0, NULL, 0, NULL, NULL, &ipg_clk);
DEFINE_CLOCK(ipg_clk, 0, NULL, 0, ipg_get_rate, NULL, &ahb_clk);
+DEFINE_CLOCK(ckil_clk, 0, NULL, 0, clk_ckil_get_rate, NULL, NULL);
#define _REGISTER_CLOCK(d, n, c) \
{ \
@@ -518,9 +525,9 @@ DEFINE_CLOCK(ipg_clk, 0, NULL, 0, ipg_get_rate, NULL, &ahb_clk);
static struct clk_lookup lookups[] = {
_REGISTER_CLOCK(NULL, "emi", emi_clk)
- _REGISTER_CLOCK(NULL, "cspi", cspi1_clk)
- _REGISTER_CLOCK(NULL, "cspi", cspi2_clk)
- _REGISTER_CLOCK(NULL, "cspi", cspi3_clk)
+ _REGISTER_CLOCK("spi_imx.0", NULL, cspi1_clk)
+ _REGISTER_CLOCK("spi_imx.1", NULL, cspi2_clk)
+ _REGISTER_CLOCK("spi_imx.2", NULL, cspi3_clk)
_REGISTER_CLOCK(NULL, "gpt", gpt_clk)
_REGISTER_CLOCK(NULL, "pwm", pwm_clk)
_REGISTER_CLOCK(NULL, "wdog", wdog_clk)
@@ -531,6 +538,12 @@ static struct clk_lookup lookups[] = {
_REGISTER_CLOCK("ipu-core", NULL, ipu_clk)
_REGISTER_CLOCK("mx3_sdc_fb", NULL, ipu_clk)
_REGISTER_CLOCK(NULL, "kpp", kpp_clk)
+ _REGISTER_CLOCK("mxc-ehci.0", "usb", usb_clk1)
+ _REGISTER_CLOCK("mxc-ehci.0", "usb_ahb", usb_clk2)
+ _REGISTER_CLOCK("mxc-ehci.1", "usb", usb_clk1)
+ _REGISTER_CLOCK("mxc-ehci.1", "usb_ahb", usb_clk2)
+ _REGISTER_CLOCK("mxc-ehci.2", "usb", usb_clk1)
+ _REGISTER_CLOCK("mxc-ehci.2", "usb_ahb", usb_clk2)
_REGISTER_CLOCK("fsl-usb2-udc", "usb", usb_clk1)
_REGISTER_CLOCK("fsl-usb2-udc", "usb_ahb", usb_clk2)
_REGISTER_CLOCK("mx3-camera.0", NULL, csi_clk)
@@ -559,6 +572,7 @@ static struct clk_lookup lookups[] = {
_REGISTER_CLOCK(NULL, "iim", iim_clk)
_REGISTER_CLOCK(NULL, "mpeg4", mpeg4_clk)
_REGISTER_CLOCK(NULL, "mbx", mbx_clk)
+ _REGISTER_CLOCK("mxc_rtc", NULL, ckil_clk)
};
int __init mx31_clocks_init(unsigned long fref)
@@ -609,7 +623,7 @@ int __init mx31_clocks_init(unsigned long fref)
__raw_writel(reg, MXC_CCM_PMCR1);
}
- mxc_timer_init(&ipg_clk);
+ mxc_timer_init(&ipg_clk, IO_ADDRESS(GPT1_BASE_ADDR), MXC_INT_GPT);
return 0;
}
diff --git a/arch/arm/mach-mx3/devices.c b/arch/arm/mach-mx3/devices.c
index 9e87e08fb12..8a577f36725 100644
--- a/arch/arm/mach-mx3/devices.c
+++ b/arch/arm/mach-mx3/devices.c
@@ -129,19 +129,17 @@ struct platform_device mxc_uart_device4 = {
/* GPIO port description */
static struct mxc_gpio_port imx_gpio_ports[] = {
- [0] = {
+ {
.chip.label = "gpio-0",
.base = IO_ADDRESS(GPIO1_BASE_ADDR),
.irq = MXC_INT_GPIO1,
.virtual_irq_start = MXC_GPIO_IRQ_START,
- },
- [1] = {
+ }, {
.chip.label = "gpio-1",
.base = IO_ADDRESS(GPIO2_BASE_ADDR),
.irq = MXC_INT_GPIO2,
.virtual_irq_start = MXC_GPIO_IRQ_START + 32,
- },
- [2] = {
+ }, {
.chip.label = "gpio-2",
.base = IO_ADDRESS(GPIO3_BASE_ADDR),
.irq = MXC_INT_GPIO3,
@@ -173,11 +171,11 @@ static struct resource mxc_nand_resources[] = {
{
.start = 0, /* runtime dependent */
.end = 0,
- .flags = IORESOURCE_MEM
+ .flags = IORESOURCE_MEM,
}, {
.start = MXC_INT_NANDFC,
.end = MXC_INT_NANDFC,
- .flags = IORESOURCE_IRQ
+ .flags = IORESOURCE_IRQ,
},
};
@@ -193,8 +191,7 @@ static struct resource mxc_i2c0_resources[] = {
.start = I2C_BASE_ADDR,
.end = I2C_BASE_ADDR + SZ_4K - 1,
.flags = IORESOURCE_MEM,
- },
- {
+ }, {
.start = MXC_INT_I2C,
.end = MXC_INT_I2C,
.flags = IORESOURCE_IRQ,
@@ -213,8 +210,7 @@ static struct resource mxc_i2c1_resources[] = {
.start = I2C2_BASE_ADDR,
.end = I2C2_BASE_ADDR + SZ_4K - 1,
.flags = IORESOURCE_MEM,
- },
- {
+ }, {
.start = MXC_INT_I2C2,
.end = MXC_INT_I2C2,
.flags = IORESOURCE_IRQ,
@@ -233,8 +229,7 @@ static struct resource mxc_i2c2_resources[] = {
.start = I2C3_BASE_ADDR,
.end = I2C3_BASE_ADDR + SZ_4K - 1,
.flags = IORESOURCE_MEM,
- },
- {
+ }, {
.start = MXC_INT_I2C3,
.end = MXC_INT_I2C3,
.flags = IORESOURCE_IRQ,
@@ -371,8 +366,8 @@ struct platform_device mx3_camera = {
static struct resource otg_resources[] = {
{
- .start = OTG_BASE_ADDR,
- .end = OTG_BASE_ADDR + 0x1ff,
+ .start = MX31_OTG_BASE_ADDR,
+ .end = MX31_OTG_BASE_ADDR + 0x1ff,
.flags = IORESOURCE_MEM,
}, {
.start = MXC_INT_USB3,
@@ -395,16 +390,142 @@ struct platform_device mxc_otg_udc_device = {
.num_resources = ARRAY_SIZE(otg_resources),
};
+/* OTG host */
+struct platform_device mxc_otg_host = {
+ .name = "mxc-ehci",
+ .id = 0,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ .dma_mask = &otg_dmamask,
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+};
+
+/* USB host 1 */
+
+static u64 usbh1_dmamask = ~(u32)0;
+
+static struct resource mxc_usbh1_resources[] = {
+ {
+ .start = MX31_OTG_BASE_ADDR + 0x200,
+ .end = MX31_OTG_BASE_ADDR + 0x3ff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_USB1,
+ .end = MXC_INT_USB1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_usbh1 = {
+ .name = "mxc-ehci",
+ .id = 1,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ .dma_mask = &usbh1_dmamask,
+ },
+ .resource = mxc_usbh1_resources,
+ .num_resources = ARRAY_SIZE(mxc_usbh1_resources),
+};
+
+/* USB host 2 */
+static u64 usbh2_dmamask = ~(u32)0;
+
+static struct resource mxc_usbh2_resources[] = {
+ {
+ .start = MX31_OTG_BASE_ADDR + 0x400,
+ .end = MX31_OTG_BASE_ADDR + 0x5ff,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_USB2,
+ .end = MXC_INT_USB2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_usbh2 = {
+ .name = "mxc-ehci",
+ .id = 2,
+ .dev = {
+ .coherent_dma_mask = 0xffffffff,
+ .dma_mask = &usbh2_dmamask,
+ },
+ .resource = mxc_usbh2_resources,
+ .num_resources = ARRAY_SIZE(mxc_usbh2_resources),
+};
+
+/*
+ * SPI master controller
+ * 3 channels
+ */
+static struct resource imx_spi_0_resources[] = {
+ {
+ .start = CSPI1_BASE_ADDR,
+ .end = CSPI1_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_CSPI1,
+ .end = MXC_INT_CSPI1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource imx_spi_1_resources[] = {
+ {
+ .start = CSPI2_BASE_ADDR,
+ .end = CSPI2_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_CSPI2,
+ .end = MXC_INT_CSPI2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource imx_spi_2_resources[] = {
+ {
+ .start = CSPI3_BASE_ADDR,
+ .end = CSPI3_BASE_ADDR + SZ_4K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC_INT_CSPI3,
+ .end = MXC_INT_CSPI3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device imx_spi_device0 = {
+ .name = "spi_imx",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(imx_spi_0_resources),
+ .resource = imx_spi_0_resources,
+};
+
+struct platform_device imx_spi_device1 = {
+ .name = "spi_imx",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(imx_spi_1_resources),
+ .resource = imx_spi_1_resources,
+};
+
+struct platform_device imx_spi_device2 = {
+ .name = "spi_imx",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(imx_spi_2_resources),
+ .resource = imx_spi_2_resources,
+};
+
#ifdef CONFIG_ARCH_MX35
static struct resource mxc_fec_resources[] = {
{
.start = MXC_FEC_BASE_ADDR,
.end = MXC_FEC_BASE_ADDR + 0xfff,
- .flags = IORESOURCE_MEM
+ .flags = IORESOURCE_MEM,
}, {
.start = MXC_INT_FEC,
.end = MXC_INT_FEC,
- .flags = IORESOURCE_IRQ
+ .flags = IORESOURCE_IRQ,
},
};
@@ -426,6 +547,14 @@ static int mx3_devices_init(void)
if (cpu_is_mx35()) {
mxc_nand_resources[0].start = MX35_NFC_BASE_ADDR;
mxc_nand_resources[0].end = MX35_NFC_BASE_ADDR + 0xfff;
+ otg_resources[0].start = MX35_OTG_BASE_ADDR;
+ otg_resources[0].end = MX35_OTG_BASE_ADDR + 0x1ff;
+ otg_resources[1].start = MXC_INT_USBOTG;
+ otg_resources[1].end = MXC_INT_USBOTG;
+ mxc_usbh1_resources[0].start = MX35_OTG_BASE_ADDR + 0x400;
+ mxc_usbh1_resources[0].end = MX35_OTG_BASE_ADDR + 0x5ff;
+ mxc_usbh1_resources[1].start = MXC_INT_USBHS;
+ mxc_usbh1_resources[1].end = MXC_INT_USBHS;
}
return 0;
diff --git a/arch/arm/mach-mx3/devices.h b/arch/arm/mach-mx3/devices.h
index ffd494ddd4a..79f2be45d13 100644
--- a/arch/arm/mach-mx3/devices.h
+++ b/arch/arm/mach-mx3/devices.h
@@ -16,5 +16,11 @@ extern struct platform_device mxc_fec_device;
extern struct platform_device mxcsdhc_device0;
extern struct platform_device mxcsdhc_device1;
extern struct platform_device mxc_otg_udc_device;
+extern struct platform_device mxc_otg_host;
+extern struct platform_device mxc_usbh1;
+extern struct platform_device mxc_usbh2;
extern struct platform_device mxc_rnga_device;
+extern struct platform_device imx_spi_device0;
+extern struct platform_device imx_spi_device1;
+extern struct platform_device imx_spi_device2;
diff --git a/arch/arm/mach-mx3/mm.c b/arch/arm/mach-mx3/mm.c
index 1f5fdd456cb..ad5a1122d76 100644
--- a/arch/arm/mach-mx3/mm.c
+++ b/arch/arm/mach-mx3/mm.c
@@ -30,6 +30,7 @@
#include <mach/common.h>
#include <mach/hardware.h>
+#include <mach/iomux-v3.h>
/*!
* @file mm.c
@@ -75,6 +76,7 @@ static struct map_desc mxc_io_desc[] __initdata = {
void __init mx31_map_io(void)
{
mxc_set_cpu_type(MXC_CPU_MX31);
+ mxc_arch_reset_init(IO_ADDRESS(WDOG_BASE_ADDR));
iotable_init(mxc_io_desc, ARRAY_SIZE(mxc_io_desc));
}
@@ -82,10 +84,22 @@ void __init mx31_map_io(void)
void __init mx35_map_io(void)
{
mxc_set_cpu_type(MXC_CPU_MX35);
+ mxc_iomux_v3_init(IO_ADDRESS(IOMUXC_BASE_ADDR));
+ mxc_arch_reset_init(IO_ADDRESS(WDOG_BASE_ADDR));
iotable_init(mxc_io_desc, ARRAY_SIZE(mxc_io_desc));
}
+void __init mx31_init_irq(void)
+{
+ mxc_init_irq(IO_ADDRESS(AVIC_BASE_ADDR));
+}
+
+void __init mx35_init_irq(void)
+{
+ mx31_init_irq();
+}
+
#ifdef CONFIG_CACHE_L2X0
static int mxc_init_l2x0(void)
{
diff --git a/arch/arm/mach-mx3/mx31ads.c b/arch/arm/mach-mx3/mx31ads.c
index 30e2767a78a..0497c152be1 100644
--- a/arch/arm/mach-mx3/mx31ads.c
+++ b/arch/arm/mach-mx3/mx31ads.c
@@ -517,7 +517,7 @@ static void __init mx31ads_map_io(void)
static void __init mx31ads_init_irq(void)
{
- mxc_init_irq();
+ mx31_init_irq();
mx31ads_init_expio();
}
diff --git a/arch/arm/mach-mx3/mx31lilly.c b/arch/arm/mach-mx3/mx31lilly.c
index 6ab2f163cb9..423025150f6 100644
--- a/arch/arm/mach-mx3/mx31lilly.c
+++ b/arch/arm/mach-mx3/mx31lilly.c
@@ -148,7 +148,7 @@ MACHINE_START(LILLY1131, "INCO startec LILLY-1131")
.io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx31_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx31_init_irq,
.init_machine = mx31lilly_board_init,
.timer = &mx31lilly_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx3/mx31lite.c b/arch/arm/mach-mx3/mx31lite.c
index 86fe70fa3e1..a8d57decdfd 100644
--- a/arch/arm/mach-mx3/mx31lite.c
+++ b/arch/arm/mach-mx3/mx31lite.c
@@ -71,12 +71,11 @@ static struct smsc911x_platform_config smsc911x_config = {
};
static struct resource smsc911x_resources[] = {
- [0] = {
+ {
.start = CS4_BASE_ADDR,
.end = CS4_BASE_ADDR + 0x100,
.flags = IORESOURCE_MEM,
- },
- [1] = {
+ }, {
.start = IOMUX_TO_IRQ(MX31_PIN_SFS6),
.end = IOMUX_TO_IRQ(MX31_PIN_SFS6),
.flags = IORESOURCE_IRQ,
@@ -162,7 +161,7 @@ MACHINE_START(MX31LITE, "LogicPD MX31 LITEKIT")
.io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx31lite_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx31_init_irq,
.init_machine = mxc_board_init,
.timer = &mx31lite_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx3/mx31moboard-devboard.c b/arch/arm/mach-mx3/mx31moboard-devboard.c
index b48581e7ded..5592cdb8d0a 100644
--- a/arch/arm/mach-mx3/mx31moboard-devboard.c
+++ b/arch/arm/mach-mx3/mx31moboard-devboard.c
@@ -16,7 +16,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/fsl_devices.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -40,18 +39,6 @@ static unsigned int devboard_pins[] = {
MX31_PIN_PC_READY__SD2_DATA1, MX31_PIN_PC_WAIT_B__SD2_DATA0,
MX31_PIN_PC_CD2_B__SD2_CLK, MX31_PIN_PC_CD1_B__SD2_CMD,
MX31_PIN_ATA_DIOR__GPIO3_28, MX31_PIN_ATA_DIOW__GPIO3_29,
- /* USB OTG */
- MX31_PIN_USBOTG_DATA0__USBOTG_DATA0,
- MX31_PIN_USBOTG_DATA1__USBOTG_DATA1,
- MX31_PIN_USBOTG_DATA2__USBOTG_DATA2,
- MX31_PIN_USBOTG_DATA3__USBOTG_DATA3,
- MX31_PIN_USBOTG_DATA4__USBOTG_DATA4,
- MX31_PIN_USBOTG_DATA5__USBOTG_DATA5,
- MX31_PIN_USBOTG_DATA6__USBOTG_DATA6,
- MX31_PIN_USBOTG_DATA7__USBOTG_DATA7,
- MX31_PIN_USBOTG_CLK__USBOTG_CLK, MX31_PIN_USBOTG_DIR__USBOTG_DIR,
- MX31_PIN_USBOTG_NXT__USBOTG_NXT, MX31_PIN_USBOTG_STP__USBOTG_STP,
- MX31_PIN_USB_OC__GPIO1_30,
};
static struct imxuart_platform_data uart_pdata = {
@@ -111,33 +98,6 @@ static struct imxmmc_platform_data sdhc2_pdata = {
.exit = devboard_sdhc2_exit,
};
-static struct fsl_usb2_platform_data usb_pdata = {
- .operating_mode = FSL_USB2_DR_DEVICE,
- .phy_mode = FSL_USB2_PHY_ULPI,
-};
-
-#define OTG_PAD_CFG (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST)
-#define OTG_EN_B IOMUX_TO_GPIO(MX31_PIN_USB_OC)
-
-static void devboard_usbotg_init(void)
-{
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA0, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA1, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA2, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA3, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA4, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA5, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA6, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA7, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_CLK, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DIR, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_NXT, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_STP, OTG_PAD_CFG);
-
- gpio_request(OTG_EN_B, "usb-udc-en");
- gpio_direction_output(OTG_EN_B, 0);
-}
-
/*
* system init for baseboard usage. Will be called by mx31moboard init.
*/
@@ -151,7 +111,4 @@ void __init mx31moboard_devboard_init(void)
mxc_register_device(&mxc_uart_device1, &uart_pdata);
mxc_register_device(&mxcsdhc_device1, &sdhc2_pdata);
-
- devboard_usbotg_init();
- mxc_register_device(&mxc_otg_udc_device, &usb_pdata);
}
diff --git a/arch/arm/mach-mx3/mx31moboard-marxbot.c b/arch/arm/mach-mx3/mx31moboard-marxbot.c
index 901fb0166c0..2bfaffb344f 100644
--- a/arch/arm/mach-mx3/mx31moboard-marxbot.c
+++ b/arch/arm/mach-mx3/mx31moboard-marxbot.c
@@ -16,7 +16,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/fsl_devices.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -48,18 +47,8 @@ static unsigned int marxbot_pins[] = {
MX31_PIN_CSI_PIXCLK__CSI_PIXCLK, MX31_PIN_CSI_VSYNC__CSI_VSYNC,
MX31_PIN_GPIO3_0__GPIO3_0, MX31_PIN_GPIO3_1__GPIO3_1,
MX31_PIN_TXD2__GPIO1_28,
- /* USB OTG */
- MX31_PIN_USBOTG_DATA0__USBOTG_DATA0,
- MX31_PIN_USBOTG_DATA1__USBOTG_DATA1,
- MX31_PIN_USBOTG_DATA2__USBOTG_DATA2,
- MX31_PIN_USBOTG_DATA3__USBOTG_DATA3,
- MX31_PIN_USBOTG_DATA4__USBOTG_DATA4,
- MX31_PIN_USBOTG_DATA5__USBOTG_DATA5,
- MX31_PIN_USBOTG_DATA6__USBOTG_DATA6,
- MX31_PIN_USBOTG_DATA7__USBOTG_DATA7,
- MX31_PIN_USBOTG_CLK__USBOTG_CLK, MX31_PIN_USBOTG_DIR__USBOTG_DIR,
- MX31_PIN_USBOTG_NXT__USBOTG_NXT, MX31_PIN_USBOTG_STP__USBOTG_STP,
- MX31_PIN_USB_OC__GPIO1_30,
+ /* dsPIC resets */
+ MX31_PIN_STXD5__GPIO1_21, MX31_PIN_SRXD5__GPIO1_22,
};
#define SDHC2_CD IOMUX_TO_GPIO(MX31_PIN_ATA_DIOR)
@@ -115,31 +104,20 @@ static struct imxmmc_platform_data sdhc2_pdata = {
.exit = marxbot_sdhc2_exit,
};
-static struct fsl_usb2_platform_data usb_pdata = {
- .operating_mode = FSL_USB2_DR_DEVICE,
- .phy_mode = FSL_USB2_PHY_ULPI,
-};
-
-#define OTG_PAD_CFG (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST)
-#define OTG_EN_B IOMUX_TO_GPIO(MX31_PIN_USB_OC)
+#define TRSLAT_RST_B IOMUX_TO_GPIO(MX31_PIN_STXD5)
+#define DSPICS_RST_B IOMUX_TO_GPIO(MX31_PIN_SRXD5)
-static void marxbot_usbotg_init(void)
+static void dspics_resets_init(void)
{
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA0, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA1, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA2, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA3, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA4, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA5, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA6, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA7, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_CLK, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_DIR, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_NXT, OTG_PAD_CFG);
- mxc_iomux_set_pad(MX31_PIN_USBOTG_STP, OTG_PAD_CFG);
-
- gpio_request(OTG_EN_B, "usb-udc-en");
- gpio_direction_output(OTG_EN_B, 0);
+ if (!gpio_request(TRSLAT_RST_B, "translator-rst")) {
+ gpio_direction_output(TRSLAT_RST_B, 1);
+ gpio_export(TRSLAT_RST_B, false);
+ }
+
+ if (!gpio_request(DSPICS_RST_B, "dspics-rst")) {
+ gpio_direction_output(DSPICS_RST_B, 1);
+ gpio_export(DSPICS_RST_B, false);
+ }
}
/*
@@ -152,8 +130,7 @@ void __init mx31moboard_marxbot_init(void)
mxc_iomux_setup_multiple_pins(marxbot_pins, ARRAY_SIZE(marxbot_pins),
"marxbot");
- mxc_register_device(&mxcsdhc_device1, &sdhc2_pdata);
+ dspics_resets_init();
- marxbot_usbotg_init();
- mxc_register_device(&mxc_otg_udc_device, &usb_pdata);
+ mxc_register_device(&mxcsdhc_device1, &sdhc2_pdata);
}
diff --git a/arch/arm/mach-mx3/mx31moboard.c b/arch/arm/mach-mx3/mx31moboard.c
index 2a2da4739ec..9243de54041 100644
--- a/arch/arm/mach-mx3/mx31moboard.c
+++ b/arch/arm/mach-mx3/mx31moboard.c
@@ -16,9 +16,12 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/delay.h>
+#include <linux/fsl_devices.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
+#include <linux/leds.h>
#include <linux/memory.h>
#include <linux/mtd/physmap.h>
#include <linux/mtd/partitions.h>
@@ -36,6 +39,7 @@
#include <mach/iomux-mx3.h>
#include <mach/i2c.h>
#include <mach/mmc.h>
+#include <mach/mx31.h>
#include "devices.h"
@@ -55,6 +59,26 @@ static unsigned int moboard_pins[] = {
MX31_PIN_SD1_DATA1__SD1_DATA1, MX31_PIN_SD1_DATA0__SD1_DATA0,
MX31_PIN_SD1_CLK__SD1_CLK, MX31_PIN_SD1_CMD__SD1_CMD,
MX31_PIN_ATA_CS0__GPIO3_26, MX31_PIN_ATA_CS1__GPIO3_27,
+ /* USB reset */
+ MX31_PIN_GPIO1_0__GPIO1_0,
+ /* USB OTG */
+ MX31_PIN_USBOTG_DATA0__USBOTG_DATA0,
+ MX31_PIN_USBOTG_DATA1__USBOTG_DATA1,
+ MX31_PIN_USBOTG_DATA2__USBOTG_DATA2,
+ MX31_PIN_USBOTG_DATA3__USBOTG_DATA3,
+ MX31_PIN_USBOTG_DATA4__USBOTG_DATA4,
+ MX31_PIN_USBOTG_DATA5__USBOTG_DATA5,
+ MX31_PIN_USBOTG_DATA6__USBOTG_DATA6,
+ MX31_PIN_USBOTG_DATA7__USBOTG_DATA7,
+ MX31_PIN_USBOTG_CLK__USBOTG_CLK, MX31_PIN_USBOTG_DIR__USBOTG_DIR,
+ MX31_PIN_USBOTG_NXT__USBOTG_NXT, MX31_PIN_USBOTG_STP__USBOTG_STP,
+ MX31_PIN_USB_OC__GPIO1_30,
+ /* LEDs */
+ MX31_PIN_SVEN0__GPIO2_0, MX31_PIN_STX0__GPIO2_1,
+ MX31_PIN_SRX0__GPIO2_2, MX31_PIN_SIMPD0__GPIO2_3,
+ /* SEL */
+ MX31_PIN_DTR_DCE1__GPIO2_8, MX31_PIN_DSR_DCE1__GPIO2_9,
+ MX31_PIN_RI_DCE1__GPIO2_10, MX31_PIN_DCD_DCE1__GPIO2_11,
};
static struct physmap_flash_data mx31moboard_flash_data = {
@@ -142,8 +166,109 @@ static struct imxmmc_platform_data sdhc1_pdata = {
.exit = moboard_sdhc1_exit,
};
+/*
+ * this pin is dedicated for all mx31moboard systems, so we do it here
+ */
+#define USB_RESET_B IOMUX_TO_GPIO(MX31_PIN_GPIO1_0)
+
+static void usb_xcvr_reset(void)
+{
+ gpio_request(USB_RESET_B, "usb-reset");
+ gpio_direction_output(USB_RESET_B, 0);
+ mdelay(1);
+ gpio_set_value(USB_RESET_B, 1);
+}
+
+#define USB_PAD_CFG (PAD_CTL_DRV_MAX | PAD_CTL_SRE_FAST | PAD_CTL_HYS_CMOS | \
+ PAD_CTL_ODE_CMOS | PAD_CTL_100K_PU)
+
+#define OTG_EN_B IOMUX_TO_GPIO(MX31_PIN_USB_OC)
+
+static void moboard_usbotg_init(void)
+{
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA0, USB_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA1, USB_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA2, USB_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA3, USB_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA4, USB_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA5, USB_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA6, USB_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DATA7, USB_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_CLK, USB_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_DIR, USB_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_NXT, USB_PAD_CFG);
+ mxc_iomux_set_pad(MX31_PIN_USBOTG_STP, USB_PAD_CFG);
+
+ gpio_request(OTG_EN_B, "usb-udc-en");
+ gpio_direction_output(OTG_EN_B, 0);
+}
+
+static struct fsl_usb2_platform_data usb_pdata = {
+ .operating_mode = FSL_USB2_DR_DEVICE,
+ .phy_mode = FSL_USB2_PHY_ULPI,
+};
+
+static struct gpio_led mx31moboard_leds[] = {
+ {
+ .name = "coreboard-led-0:red:running",
+ .default_trigger = "heartbeat",
+ .gpio = IOMUX_TO_GPIO(MX31_PIN_SVEN0),
+ }, {
+ .name = "coreboard-led-1:red",
+ .gpio = IOMUX_TO_GPIO(MX31_PIN_STX0),
+ }, {
+ .name = "coreboard-led-2:red",
+ .gpio = IOMUX_TO_GPIO(MX31_PIN_SRX0),
+ }, {
+ .name = "coreboard-led-3:red",
+ .gpio = IOMUX_TO_GPIO(MX31_PIN_SIMPD0),
+ },
+};
+
+static struct gpio_led_platform_data mx31moboard_led_pdata = {
+ .num_leds = ARRAY_SIZE(mx31moboard_leds),
+ .leds = mx31moboard_leds,
+};
+
+static struct platform_device mx31moboard_leds_device = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &mx31moboard_led_pdata,
+ },
+};
+
+#define SEL0 IOMUX_TO_GPIO(MX31_PIN_DTR_DCE1)
+#define SEL1 IOMUX_TO_GPIO(MX31_PIN_DSR_DCE1)
+#define SEL2 IOMUX_TO_GPIO(MX31_PIN_RI_DCE1)
+#define SEL3 IOMUX_TO_GPIO(MX31_PIN_DCD_DCE1)
+
+static void mx31moboard_init_sel_gpios(void)
+{
+ if (!gpio_request(SEL0, "sel0")) {
+ gpio_direction_input(SEL0);
+ gpio_export(SEL0, true);
+ }
+
+ if (!gpio_request(SEL1, "sel1")) {
+ gpio_direction_input(SEL1);
+ gpio_export(SEL1, true);
+ }
+
+ if (!gpio_request(SEL2, "sel2")) {
+ gpio_direction_input(SEL2);
+ gpio_export(SEL2, true);
+ }
+
+ if (!gpio_request(SEL3, "sel3")) {
+ gpio_direction_input(SEL3);
+ gpio_export(SEL3, true);
+ }
+}
+
static struct platform_device *devices[] __initdata = {
&mx31moboard_flash,
+ &mx31moboard_leds_device,
};
static int mx31moboard_baseboard;
@@ -162,11 +287,18 @@ static void __init mxc_board_init(void)
mxc_register_device(&mxc_uart_device0, &uart_pdata);
mxc_register_device(&mxc_uart_device4, &uart_pdata);
+ mx31moboard_init_sel_gpios();
+
mxc_register_device(&mxc_i2c_device0, &moboard_i2c0_pdata);
mxc_register_device(&mxc_i2c_device1, &moboard_i2c1_pdata);
mxc_register_device(&mxcsdhc_device0, &sdhc1_pdata);
+ usb_xcvr_reset();
+
+ moboard_usbotg_init();
+ mxc_register_device(&mxc_otg_udc_device, &usb_pdata);
+
switch (mx31moboard_baseboard) {
case MX31NOBOARD:
break;
@@ -197,7 +329,7 @@ MACHINE_START(MX31MOBOARD, "EPFL Mobots mx31moboard")
.io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx31_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx31_init_irq,
.init_machine = mxc_board_init,
.timer = &mx31moboard_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx3/mx31pdk.c b/arch/arm/mach-mx3/mx31pdk.c
index c19838d2e36..0f7a2f06bc2 100644
--- a/arch/arm/mach-mx3/mx31pdk.c
+++ b/arch/arm/mach-mx3/mx31pdk.c
@@ -265,7 +265,7 @@ MACHINE_START(MX31_3DS, "Freescale MX31PDK (3DS)")
.io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx31pdk_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx31_init_irq,
.init_machine = mxc_board_init,
.timer = &mx31pdk_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx3/mx35pdk.c b/arch/arm/mach-mx3/mx35pdk.c
index 6d15374414b..6ff186e46ce 100644
--- a/arch/arm/mach-mx3/mx35pdk.c
+++ b/arch/arm/mach-mx3/mx35pdk.c
@@ -98,7 +98,7 @@ MACHINE_START(MX35_3DS, "Freescale MX35PDK")
.io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx35_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx35_init_irq,
.init_machine = mxc_board_init,
.timer = &mx35pdk_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx3/pcm037.c b/arch/arm/mach-mx3/pcm037.c
index 840cfda341d..6cbaabedf38 100644
--- a/arch/arm/mach-mx3/pcm037.c
+++ b/arch/arm/mach-mx3/pcm037.c
@@ -32,6 +32,7 @@
#include <linux/spi/spi.h>
#include <linux/irq.h>
#include <linux/fsl_devices.h>
+#include <linux/can/platform/sja1000.h>
#include <media/soc_camera.h>
@@ -169,6 +170,8 @@ static unsigned int pcm037_pins[] = {
MX31_PIN_CSI_MCLK__CSI_MCLK,
MX31_PIN_CSI_PIXCLK__CSI_PIXCLK,
MX31_PIN_CSI_VSYNC__CSI_VSYNC,
+ /* GPIO */
+ IOMUX_MODE(MX31_PIN_ATA_DMACK, IOMUX_CONFIG_GPIO),
};
static struct physmap_flash_data pcm037_flash_data = {
@@ -244,12 +247,11 @@ static struct imxuart_platform_data uart_pdata = {
};
static struct resource smsc911x_resources[] = {
- [0] = {
+ {
.start = CS1_BASE_ADDR + 0x300,
.end = CS1_BASE_ADDR + 0x300 + SZ_64K - 1,
.flags = IORESOURCE_MEM,
- },
- [1] = {
+ }, {
.start = IOMUX_TO_IRQ(MX31_PIN_GPIO3_1),
.end = IOMUX_TO_IRQ(MX31_PIN_GPIO3_1),
.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL,
@@ -339,8 +341,7 @@ static struct i2c_board_info pcm037_i2c_devices[] = {
I2C_BOARD_INFO("at24", 0x52), /* E0=0, E1=1, E2=0 */
.platform_data = &board_eeprom,
}, {
- I2C_BOARD_INFO("rtc-pcf8563", 0x51),
- .type = "pcf8563",
+ I2C_BOARD_INFO("pcf8563", 0x51),
}
};
@@ -515,6 +516,33 @@ static struct mx3fb_platform_data mx3fb_pdata = {
.num_modes = ARRAY_SIZE(fb_modedb),
};
+static struct resource pcm970_sja1000_resources[] = {
+ {
+ .start = CS5_BASE_ADDR,
+ .end = CS5_BASE_ADDR + 0x100 - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = IOMUX_TO_IRQ(IOMUX_PIN(48, 105)),
+ .end = IOMUX_TO_IRQ(IOMUX_PIN(48, 105)),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWEDGE,
+ },
+};
+
+struct sja1000_platform_data pcm970_sja1000_platform_data = {
+ .clock = 16000000 / 2,
+ .ocr = 0x40 | 0x18,
+ .cdr = 0x40,
+};
+
+static struct platform_device pcm970_sja1000 = {
+ .name = "sja1000_platform",
+ .dev = {
+ .platform_data = &pcm970_sja1000_platform_data,
+ },
+ .resource = pcm970_sja1000_resources,
+ .num_resources = ARRAY_SIZE(pcm970_sja1000_resources),
+};
+
/*
* Board specific initialization.
*/
@@ -575,6 +603,8 @@ static void __init mxc_board_init(void)
if (!pcm037_camera_alloc_dma(4 * 1024 * 1024))
mxc_register_device(&mx3_camera, &camera_pdata);
+
+ platform_device_register(&pcm970_sja1000);
}
static void __init pcm037_timer_init(void)
@@ -592,7 +622,7 @@ MACHINE_START(PCM037, "Phytec Phycore pcm037")
.io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx31_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx31_init_irq,
.init_machine = mxc_board_init,
.timer = &pcm037_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx3/pcm043.c b/arch/arm/mach-mx3/pcm043.c
index 8d27c324abf..e18a224671f 100644
--- a/arch/arm/mach-mx3/pcm043.c
+++ b/arch/arm/mach-mx3/pcm043.c
@@ -133,8 +133,7 @@ static struct i2c_board_info pcm043_i2c_devices[] = {
I2C_BOARD_INFO("at24", 0x52), /* E0=0, E1=1, E2=0 */
.platform_data = &board_eeprom,
}, {
- I2C_BOARD_INFO("rtc-pcf8563", 0x51),
- .type = "pcf8563",
+ I2C_BOARD_INFO("pcf8563", 0x51),
}
};
#endif
@@ -203,7 +202,8 @@ static struct pad_desc pcm043_pads[] = {
MX35_PAD_D3_VSYNC__IPU_DISPB_D3_VSYNC,
MX35_PAD_D3_REV__IPU_DISPB_D3_REV,
MX35_PAD_D3_CLS__IPU_DISPB_D3_CLS,
- MX35_PAD_D3_SPL__IPU_DISPB_D3_SPL
+ /* gpio */
+ MX35_PAD_ATA_CS0__GPIO2_6,
};
/*
@@ -245,7 +245,7 @@ MACHINE_START(PCM043, "Phytec Phycore pcm043")
.io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx35_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx35_init_irq,
.init_machine = mxc_board_init,
.timer = &pcm043_timer,
MACHINE_END
diff --git a/arch/arm/mach-mx3/qong.c b/arch/arm/mach-mx3/qong.c
index 82b31c4ab11..044511f1b9a 100644
--- a/arch/arm/mach-mx3/qong.c
+++ b/arch/arm/mach-mx3/qong.c
@@ -81,13 +81,12 @@ static inline void mxc_init_imx_uart(void)
}
static struct resource dnet_resources[] = {
- [0] = {
+ {
.name = "dnet-memory",
.start = QONG_DNET_BASEADDR,
.end = QONG_DNET_BASEADDR + QONG_DNET_SIZE - 1,
.flags = IORESOURCE_MEM,
- },
- [1] = {
+ }, {
.start = QONG_FPGA_IRQ,
.end = QONG_FPGA_IRQ,
.flags = IORESOURCE_IRQ,
@@ -280,7 +279,7 @@ MACHINE_START(QONG, "Dave/DENX QongEVB-LITE")
.io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.boot_params = PHYS_OFFSET + 0x100,
.map_io = mx31_map_io,
- .init_irq = mxc_init_irq,
+ .init_irq = mx31_init_irq,
.init_machine = mxc_board_init,
.timer = &qong_timer,
MACHINE_END
diff --git a/arch/arm/mach-mxc91231/Kconfig b/arch/arm/mach-mxc91231/Kconfig
new file mode 100644
index 00000000000..8e5fa38ebb6
--- /dev/null
+++ b/arch/arm/mach-mxc91231/Kconfig
@@ -0,0 +1,11 @@
+if ARCH_MXC91231
+
+comment "MXC91231 platforms:"
+
+config MACH_MAGX_ZN5
+ bool "Support Motorola Zn5 GSM phone"
+ default n
+ help
+ Include support for Motorola Zn5 GSM phone.
+
+endif
diff --git a/arch/arm/mach-mxc91231/Makefile b/arch/arm/mach-mxc91231/Makefile
new file mode 100644
index 00000000000..011d5e19712
--- /dev/null
+++ b/arch/arm/mach-mxc91231/Makefile
@@ -0,0 +1,2 @@
+obj-y := mm.o clock.o devices.o system.o iomux.o
+obj-$(CONFIG_MACH_MAGX_ZN5) += magx-zn5.o
diff --git a/arch/arm/mach-mxc91231/Makefile.boot b/arch/arm/mach-mxc91231/Makefile.boot
new file mode 100644
index 00000000000..9939a19d99a
--- /dev/null
+++ b/arch/arm/mach-mxc91231/Makefile.boot
@@ -0,0 +1,3 @@
+ zreladdr-y := 0x90008000
+params_phys-y := 0x90000100
+initrd_phys-y := 0x90800000
diff --git a/arch/arm/mach-mxc91231/clock.c b/arch/arm/mach-mxc91231/clock.c
new file mode 100644
index 00000000000..ecfa37fef8a
--- /dev/null
+++ b/arch/arm/mach-mxc91231/clock.c
@@ -0,0 +1,642 @@
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <mach/clock.h>
+#include <mach/hardware.h>
+#include <mach/common.h>
+
+#include <asm/clkdev.h>
+#include <asm/bug.h>
+#include <asm/div64.h>
+
+#include "crm_regs.h"
+
+#define CRM_SMALL_DIVIDER(base, name) \
+ crm_small_divider(base, \
+ base ## _ ## name ## _OFFSET, \
+ base ## _ ## name ## _MASK)
+#define CRM_1DIVIDER(base, name) \
+ crm_divider(base, \
+ base ## _ ## name ## _OFFSET, \
+ base ## _ ## name ## _MASK, 1)
+#define CRM_16DIVIDER(base, name) \
+ crm_divider(base, \
+ base ## _ ## name ## _OFFSET, \
+ base ## _ ## name ## _MASK, 16)
+
+static u32 crm_small_divider(void __iomem *reg, u8 offset, u32 mask)
+{
+ static const u32 crm_small_dividers[] = {
+ 2, 3, 4, 5, 6, 8, 10, 12
+ };
+ u8 idx;
+
+ idx = (__raw_readl(reg) & mask) >> offset;
+ if (idx > 7)
+ return 1;
+
+ return crm_small_dividers[idx];
+}
+
+static u32 crm_divider(void __iomem *reg, u8 offset, u32 mask, u32 z)
+{
+ u32 div;
+ div = (__raw_readl(reg) & mask) >> offset;
+ return div ? div : z;
+}
+
+static int _clk_1bit_enable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg |= 1 << clk->enable_shift;
+ __raw_writel(reg, clk->enable_reg);
+
+ return 0;
+}
+
+static void _clk_1bit_disable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(1 << clk->enable_shift);
+ __raw_writel(reg, clk->enable_reg);
+}
+
+static int _clk_3bit_enable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg |= 0x7 << clk->enable_shift;
+ __raw_writel(reg, clk->enable_reg);
+
+ return 0;
+}
+
+static void _clk_3bit_disable(struct clk *clk)
+{
+ u32 reg;
+
+ reg = __raw_readl(clk->enable_reg);
+ reg &= ~(0x7 << clk->enable_shift);
+ __raw_writel(reg, clk->enable_reg);
+}
+
+static unsigned long ckih_rate;
+
+static unsigned long clk_ckih_get_rate(struct clk *clk)
+{
+ return ckih_rate;
+}
+
+static struct clk ckih_clk = {
+ .get_rate = clk_ckih_get_rate,
+};
+
+static unsigned long clk_ckih_x2_get_rate(struct clk *clk)
+{
+ return 2 * clk_get_rate(clk->parent);
+}
+
+static struct clk ckih_x2_clk = {
+ .parent = &ckih_clk,
+ .get_rate = clk_ckih_x2_get_rate,
+};
+
+static unsigned long clk_ckil_get_rate(struct clk *clk)
+{
+ return CKIL_CLK_FREQ;
+}
+
+static struct clk ckil_clk = {
+ .get_rate = clk_ckil_get_rate,
+};
+
+/* plls stuff */
+static struct clk mcu_pll_clk;
+static struct clk dsp_pll_clk;
+static struct clk usb_pll_clk;
+
+static struct clk *pll_clk(u8 sel)
+{
+ switch (sel) {
+ case 0:
+ return &mcu_pll_clk;
+ case 1:
+ return &dsp_pll_clk;
+ case 2:
+ return &usb_pll_clk;
+ }
+ BUG();
+}
+
+static void __iomem *pll_base(struct clk *clk)
+{
+ if (clk == &mcu_pll_clk)
+ return MXC_PLL0_BASE;
+ else if (clk == &dsp_pll_clk)
+ return MXC_PLL1_BASE;
+ else if (clk == &usb_pll_clk)
+ return MXC_PLL2_BASE;
+ BUG();
+}
+
+static unsigned long clk_pll_get_rate(struct clk *clk)
+{
+ const void __iomem *pllbase;
+ unsigned long dp_op, dp_mfd, dp_mfn, pll_hfsm, ref_clk, mfi;
+ long mfn, mfn_abs, mfd, pdf;
+ s64 temp;
+ pllbase = pll_base(clk);
+
+ pll_hfsm = __raw_readl(pllbase + MXC_PLL_DP_CTL) & MXC_PLL_DP_CTL_HFSM;
+ if (pll_hfsm == 0) {
+ dp_op = __raw_readl(pllbase + MXC_PLL_DP_OP);
+ dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_MFD);
+ dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_MFN);
+ } else {
+ dp_op = __raw_readl(pllbase + MXC_PLL_DP_HFS_OP);
+ dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_HFS_MFD);
+ dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_HFS_MFN);
+ }
+
+ pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK;
+ mfi = (dp_op >> MXC_PLL_DP_OP_MFI_OFFSET) & MXC_PLL_DP_OP_PDF_MASK;
+ mfi = (mfi <= 5) ? 5 : mfi;
+ mfd = dp_mfd & MXC_PLL_DP_MFD_MASK;
+ mfn = dp_mfn & MXC_PLL_DP_MFN_MASK;
+ mfn = (mfn <= 0x4000000) ? mfn : (mfn - 0x10000000);
+
+ if (mfn < 0)
+ mfn_abs = -mfn;
+ else
+ mfn_abs = mfn;
+
+/* XXX: actually this asumes that ckih is fed to pll, but spec says
+ * that ckih_x2 is also possible. need to check this out.
+ */
+ ref_clk = clk_get_rate(&ckih_clk);
+
+ ref_clk *= 2;
+ ref_clk /= pdf + 1;
+
+ temp = (u64) ref_clk * mfn_abs;
+ do_div(temp, mfd);
+ if (mfn < 0)
+ temp = -temp;
+ temp += ref_clk * mfi;
+
+ return temp;
+}
+
+static int clk_pll_enable(struct clk *clk)
+{
+ void __iomem *ctl;
+ u32 reg;
+
+ ctl = pll_base(clk);
+ reg = __raw_readl(ctl);
+ reg |= (MXC_PLL_DP_CTL_RST | MXC_PLL_DP_CTL_UPEN);
+ __raw_writel(reg, ctl);
+ do {
+ reg = __raw_readl(ctl);
+ } while ((reg & MXC_PLL_DP_CTL_LRF) != MXC_PLL_DP_CTL_LRF);
+ return 0;
+}
+
+static void clk_pll_disable(struct clk *clk)
+{
+ void __iomem *ctl;
+ u32 reg;
+
+ ctl = pll_base(clk);
+ reg = __raw_readl(ctl);
+ reg &= ~(MXC_PLL_DP_CTL_RST | MXC_PLL_DP_CTL_UPEN);
+ __raw_writel(reg, ctl);
+}
+
+static struct clk mcu_pll_clk = {
+ .parent = &ckih_clk,
+ .get_rate = clk_pll_get_rate,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+};
+
+static struct clk dsp_pll_clk = {
+ .parent = &ckih_clk,
+ .get_rate = clk_pll_get_rate,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+};
+
+static struct clk usb_pll_clk = {
+ .parent = &ckih_clk,
+ .get_rate = clk_pll_get_rate,
+ .enable = clk_pll_enable,
+ .disable = clk_pll_disable,
+};
+/* plls stuff end */
+
+/* ap_ref_clk stuff */
+static struct clk ap_ref_clk;
+
+static unsigned long clk_ap_ref_get_rate(struct clk *clk)
+{
+ u32 ascsr, acsr;
+ u8 ap_pat_ref_div_2, ap_isel, acs, ads;
+
+ ascsr = __raw_readl(MXC_CRMAP_ASCSR);
+ acsr = __raw_readl(MXC_CRMAP_ACSR);
+
+ /* 0 for ckih, 1 for ckih*2 */
+ ap_isel = ascsr & MXC_CRMAP_ASCSR_APISEL;
+ /* reg divider */
+ ap_pat_ref_div_2 = (ascsr >> MXC_CRMAP_ASCSR_AP_PATDIV2_OFFSET) & 0x1;
+ /* undocumented, 1 for disabling divider */
+ ads = (acsr >> MXC_CRMAP_ACSR_ADS_OFFSET) & 0x1;
+ /* 0 for pat_ref, 1 for divider out */
+ acs = acsr & MXC_CRMAP_ACSR_ACS;
+
+ if (acs & !ads)
+ /* use divided clock */
+ return clk_get_rate(clk->parent) / (ap_pat_ref_div_2 ? 2 : 1);
+
+ return clk_get_rate(clk->parent) * (ap_isel ? 2 : 1);
+}
+
+static struct clk ap_ref_clk = {
+ .parent = &ckih_clk,
+ .get_rate = clk_ap_ref_get_rate,
+};
+/* ap_ref_clk stuff end */
+
+/* ap_pre_dfs_clk stuff */
+static struct clk ap_pre_dfs_clk;
+
+static unsigned long clk_ap_pre_dfs_get_rate(struct clk *clk)
+{
+ u32 acsr, ascsr;
+
+ acsr = __raw_readl(MXC_CRMAP_ACSR);
+ ascsr = __raw_readl(MXC_CRMAP_ASCSR);
+
+ if (acsr & MXC_CRMAP_ACSR_ACS) {
+ u8 sel;
+ sel = (ascsr & MXC_CRMAP_ASCSR_APSEL_MASK) >>
+ MXC_CRMAP_ASCSR_APSEL_OFFSET;
+ return clk_get_rate(pll_clk(sel)) /
+ CRM_SMALL_DIVIDER(MXC_CRMAP_ACDR, ARMDIV);
+ }
+ return clk_get_rate(&ap_ref_clk);
+}
+
+static struct clk ap_pre_dfs_clk = {
+ .get_rate = clk_ap_pre_dfs_get_rate,
+};
+/* ap_pre_dfs_clk stuff end */
+
+/* usb_clk stuff */
+static struct clk usb_clk;
+
+static struct clk *clk_usb_parent(struct clk *clk)
+{
+ u32 acsr, ascsr;
+
+ acsr = __raw_readl(MXC_CRMAP_ACSR);
+ ascsr = __raw_readl(MXC_CRMAP_ASCSR);
+
+ if (acsr & MXC_CRMAP_ACSR_ACS) {
+ u8 sel;
+ sel = (ascsr & MXC_CRMAP_ASCSR_USBSEL_MASK) >>
+ MXC_CRMAP_ASCSR_USBSEL_OFFSET;
+ return pll_clk(sel);
+ }
+ return &ap_ref_clk;
+}
+
+static unsigned long clk_usb_get_rate(struct clk *clk)
+{
+ return clk_get_rate(clk->parent) /
+ CRM_SMALL_DIVIDER(MXC_CRMAP_ACDER2, USBDIV);
+}
+
+static struct clk usb_clk = {
+ .enable_reg = MXC_CRMAP_ACDER2,
+ .enable_shift = MXC_CRMAP_ACDER2_USBEN_OFFSET,
+ .get_rate = clk_usb_get_rate,
+ .enable = _clk_1bit_enable,
+ .disable = _clk_1bit_disable,
+};
+/* usb_clk stuff end */
+
+static unsigned long clk_ipg_get_rate(struct clk *clk)
+{
+ return clk_get_rate(clk->parent) / CRM_16DIVIDER(MXC_CRMAP_ACDR, IPDIV);
+}
+
+static unsigned long clk_ahb_get_rate(struct clk *clk)
+{
+ return clk_get_rate(clk->parent) /
+ CRM_16DIVIDER(MXC_CRMAP_ACDR, AHBDIV);
+}
+
+static struct clk ipg_clk = {
+ .parent = &ap_pre_dfs_clk,
+ .get_rate = clk_ipg_get_rate,
+};
+
+static struct clk ahb_clk = {
+ .parent = &ap_pre_dfs_clk,
+ .get_rate = clk_ahb_get_rate,
+};
+
+/* perclk_clk stuff */
+static struct clk perclk_clk;
+
+static unsigned long clk_perclk_get_rate(struct clk *clk)
+{
+ u32 acder2;
+
+ acder2 = __raw_readl(MXC_CRMAP_ACDER2);
+ if (acder2 & MXC_CRMAP_ACDER2_BAUD_ISEL_MASK)
+ return 2 * clk_get_rate(clk->parent);
+
+ return clk_get_rate(clk->parent);
+}
+
+static struct clk perclk_clk = {
+ .parent = &ckih_clk,
+ .get_rate = clk_perclk_get_rate,
+};
+/* perclk_clk stuff end */
+
+/* uart_clk stuff */
+static struct clk uart_clk[];
+
+static unsigned long clk_uart_get_rate(struct clk *clk)
+{
+ u32 div;
+
+ switch (clk->id) {
+ case 0:
+ case 1:
+ div = CRM_SMALL_DIVIDER(MXC_CRMAP_ACDER2, BAUDDIV);
+ break;
+ case 2:
+ div = CRM_SMALL_DIVIDER(MXC_CRMAP_APRA, UART3DIV);
+ break;
+ default:
+ BUG();
+ }
+ return clk_get_rate(clk->parent) / div;
+}
+
+static struct clk uart_clk[] = {
+ {
+ .id = 0,
+ .parent = &perclk_clk,
+ .enable_reg = MXC_CRMAP_APRA,
+ .enable_shift = MXC_CRMAP_APRA_UART1EN_OFFSET,
+ .get_rate = clk_uart_get_rate,
+ .enable = _clk_1bit_enable,
+ .disable = _clk_1bit_disable,
+ }, {
+ .id = 1,
+ .parent = &perclk_clk,
+ .enable_reg = MXC_CRMAP_APRA,
+ .enable_shift = MXC_CRMAP_APRA_UART2EN_OFFSET,
+ .get_rate = clk_uart_get_rate,
+ .enable = _clk_1bit_enable,
+ .disable = _clk_1bit_disable,
+ }, {
+ .id = 2,
+ .parent = &perclk_clk,
+ .enable_reg = MXC_CRMAP_APRA,
+ .enable_shift = MXC_CRMAP_APRA_UART3EN_OFFSET,
+ .get_rate = clk_uart_get_rate,
+ .enable = _clk_1bit_enable,
+ .disable = _clk_1bit_disable,
+ },
+};
+/* uart_clk stuff end */
+
+/* sdhc_clk stuff */
+static struct clk nfc_clk;
+
+static unsigned long clk_nfc_get_rate(struct clk *clk)
+{
+ return clk_get_rate(clk->parent) /
+ CRM_1DIVIDER(MXC_CRMAP_ACDER2, NFCDIV);
+}
+
+static struct clk nfc_clk = {
+ .parent = &ahb_clk,
+ .enable_reg = MXC_CRMAP_ACDER2,
+ .enable_shift = MXC_CRMAP_ACDER2_NFCEN_OFFSET,
+ .get_rate = clk_nfc_get_rate,
+ .enable = _clk_1bit_enable,
+ .disable = _clk_1bit_disable,
+};
+/* sdhc_clk stuff end */
+
+/* sdhc_clk stuff */
+static struct clk sdhc_clk[];
+
+static struct clk *clk_sdhc_parent(struct clk *clk)
+{
+ u32 aprb;
+ u8 sel;
+ u32 mask;
+ int offset;
+
+ aprb = __raw_readl(MXC_CRMAP_APRB);
+
+ switch (clk->id) {
+ case 0:
+ mask = MXC_CRMAP_APRB_SDHC1_ISEL_MASK;
+ offset = MXC_CRMAP_APRB_SDHC1_ISEL_OFFSET;
+ break;
+ case 1:
+ mask = MXC_CRMAP_APRB_SDHC2_ISEL_MASK;
+ offset = MXC_CRMAP_APRB_SDHC2_ISEL_OFFSET;
+ break;
+ default:
+ BUG();
+ }
+ sel = (aprb & mask) >> offset;
+
+ switch (sel) {
+ case 0:
+ return &ckih_clk;
+ case 1:
+ return &ckih_x2_clk;
+ }
+ return &usb_clk;
+}
+
+static unsigned long clk_sdhc_get_rate(struct clk *clk)
+{
+ u32 div;
+
+ switch (clk->id) {
+ case 0:
+ div = CRM_SMALL_DIVIDER(MXC_CRMAP_APRB, SDHC1_DIV);
+ break;
+ case 1:
+ div = CRM_SMALL_DIVIDER(MXC_CRMAP_APRB, SDHC2_DIV);
+ break;
+ default:
+ BUG();
+ }
+
+ return clk_get_rate(clk->parent) / div;
+}
+
+static int clk_sdhc_enable(struct clk *clk)
+{
+ u32 amlpmre1, aprb;
+
+ amlpmre1 = __raw_readl(MXC_CRMAP_AMLPMRE1);
+ aprb = __raw_readl(MXC_CRMAP_APRB);
+ switch (clk->id) {
+ case 0:
+ amlpmre1 |= (0x7 << MXC_CRMAP_AMLPMRE1_MLPME4_OFFSET);
+ aprb |= (0x1 << MXC_CRMAP_APRB_SDHC1EN_OFFSET);
+ break;
+ case 1:
+ amlpmre1 |= (0x7 << MXC_CRMAP_AMLPMRE1_MLPME5_OFFSET);
+ aprb |= (0x1 << MXC_CRMAP_APRB_SDHC2EN_OFFSET);
+ break;
+ }
+ __raw_writel(amlpmre1, MXC_CRMAP_AMLPMRE1);
+ __raw_writel(aprb, MXC_CRMAP_APRB);
+ return 0;
+}
+
+static void clk_sdhc_disable(struct clk *clk)
+{
+ u32 amlpmre1, aprb;
+
+ amlpmre1 = __raw_readl(MXC_CRMAP_AMLPMRE1);
+ aprb = __raw_readl(MXC_CRMAP_APRB);
+ switch (clk->id) {
+ case 0:
+ amlpmre1 &= ~(0x7 << MXC_CRMAP_AMLPMRE1_MLPME4_OFFSET);
+ aprb &= ~(0x1 << MXC_CRMAP_APRB_SDHC1EN_OFFSET);
+ break;
+ case 1:
+ amlpmre1 &= ~(0x7 << MXC_CRMAP_AMLPMRE1_MLPME5_OFFSET);
+ aprb &= ~(0x1 << MXC_CRMAP_APRB_SDHC2EN_OFFSET);
+ break;
+ }
+ __raw_writel(amlpmre1, MXC_CRMAP_AMLPMRE1);
+ __raw_writel(aprb, MXC_CRMAP_APRB);
+}
+
+static struct clk sdhc_clk[] = {
+ {
+ .id = 0,
+ .get_rate = clk_sdhc_get_rate,
+ .enable = clk_sdhc_enable,
+ .disable = clk_sdhc_disable,
+ }, {
+ .id = 1,
+ .get_rate = clk_sdhc_get_rate,
+ .enable = clk_sdhc_enable,
+ .disable = clk_sdhc_disable,
+ },
+};
+/* sdhc_clk stuff end */
+
+/* wdog_clk stuff */
+static struct clk wdog_clk[] = {
+ {
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable_reg = MXC_CRMAP_AMLPMRD,
+ .enable_shift = MXC_CRMAP_AMLPMRD_MLPMD7_OFFSET,
+ .enable = _clk_3bit_enable,
+ .disable = _clk_3bit_disable,
+ }, {
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable_reg = MXC_CRMAP_AMLPMRD,
+ .enable_shift = MXC_CRMAP_AMLPMRD_MLPMD3_OFFSET,
+ .enable = _clk_3bit_enable,
+ .disable = _clk_3bit_disable,
+ },
+};
+/* wdog_clk stuff end */
+
+/* gpt_clk stuff */
+static struct clk gpt_clk = {
+ .parent = &ipg_clk,
+ .enable_reg = MXC_CRMAP_AMLPMRC,
+ .enable_shift = MXC_CRMAP_AMLPMRC_MLPMC4_OFFSET,
+ .enable = _clk_3bit_enable,
+ .disable = _clk_3bit_disable,
+};
+/* gpt_clk stuff end */
+
+/* cspi_clk stuff */
+static struct clk cspi_clk[] = {
+ {
+ .id = 0,
+ .parent = &ipg_clk,
+ .enable_reg = MXC_CRMAP_AMLPMRE2,
+ .enable_shift = MXC_CRMAP_AMLPMRE2_MLPME0_OFFSET,
+ .enable = _clk_3bit_enable,
+ .disable = _clk_3bit_disable,
+ }, {
+ .id = 1,
+ .parent = &ipg_clk,
+ .enable_reg = MXC_CRMAP_AMLPMRE1,
+ .enable_shift = MXC_CRMAP_AMLPMRE1_MLPME6_OFFSET,
+ .enable = _clk_3bit_enable,
+ .disable = _clk_3bit_disable,
+ },
+};
+/* cspi_clk stuff end */
+
+#define _REGISTER_CLOCK(d, n, c) \
+ { \
+ .dev_id = d, \
+ .con_id = n, \
+ .clk = &c, \
+ },
+
+static struct clk_lookup lookups[] = {
+ _REGISTER_CLOCK("imx-uart.0", NULL, uart_clk[0])
+ _REGISTER_CLOCK("imx-uart.1", NULL, uart_clk[1])
+ _REGISTER_CLOCK("imx-uart.2", NULL, uart_clk[2])
+ _REGISTER_CLOCK("mxc-mmc.0", NULL, sdhc_clk[0])
+ _REGISTER_CLOCK("mxc-mmc.1", NULL, sdhc_clk[1])
+ _REGISTER_CLOCK("mxc-wdt.0", NULL, wdog_clk[0])
+ _REGISTER_CLOCK("spi_imx.0", NULL, cspi_clk[0])
+ _REGISTER_CLOCK("spi_imx.1", NULL, cspi_clk[1])
+};
+
+int __init mxc91231_clocks_init(unsigned long fref)
+{
+ void __iomem *gpt_base;
+ int i;
+
+ ckih_rate = fref;
+
+ usb_clk.parent = clk_usb_parent(&usb_clk);
+ sdhc_clk[0].parent = clk_sdhc_parent(&sdhc_clk[0]);
+ sdhc_clk[1].parent = clk_sdhc_parent(&sdhc_clk[1]);
+
+ for (i = 0; i < ARRAY_SIZE(lookups); i++)
+ clkdev_add(&lookups[i]);
+
+ gpt_base = MXC91231_IO_ADDRESS(MXC91231_GPT1_BASE_ADDR);
+ mxc_timer_init(&gpt_clk, gpt_base, MXC91231_INT_GPT);
+
+ return 0;
+}
diff --git a/arch/arm/mach-mxc91231/crm_regs.h b/arch/arm/mach-mxc91231/crm_regs.h
new file mode 100644
index 00000000000..ce4f5905818
--- /dev/null
+++ b/arch/arm/mach-mxc91231/crm_regs.h
@@ -0,0 +1,399 @@
+/*
+ * Copyright 2006 Freescale Semiconductor, Inc.
+ * Copyright 2006-2007 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_MXC91231_CRM_REGS_H_
+#define _ARCH_ARM_MACH_MXC91231_CRM_REGS_H_
+
+#define CKIL_CLK_FREQ 32768
+
+#define MXC_CRM_AP_BASE MXC91231_IO_ADDRESS(MXC91231_CRM_AP_BASE_ADDR)
+#define MXC_CRM_COM_BASE MXC91231_IO_ADDRESS(MXC91231_CRM_COM_BASE_ADDR)
+#define MXC_DSM_BASE MXC91231_IO_ADDRESS(MXC91231_DSM_BASE_ADDR)
+#define MXC_PLL0_BASE MXC91231_IO_ADDRESS(MXC91231_PLL0_BASE_ADDR)
+#define MXC_PLL1_BASE MXC91231_IO_ADDRESS(MXC91231_PLL1_BASE_ADDR)
+#define MXC_PLL2_BASE MXC91231_IO_ADDRESS(MXC91231_PLL2_BASE_ADDR)
+#define MXC_CLKCTL_BASE MXC91231_IO_ADDRESS(MXC91231_CLKCTL_BASE_ADDR)
+
+/* PLL Register Offsets */
+#define MXC_PLL_DP_CTL 0x00
+#define MXC_PLL_DP_CONFIG 0x04
+#define MXC_PLL_DP_OP 0x08
+#define MXC_PLL_DP_MFD 0x0C
+#define MXC_PLL_DP_MFN 0x10
+#define MXC_PLL_DP_HFS_OP 0x1C
+#define MXC_PLL_DP_HFS_MFD 0x20
+#define MXC_PLL_DP_HFS_MFN 0x24
+
+/* PLL Register Bit definitions */
+#define MXC_PLL_DP_CTL_DPDCK0_2_EN 0x1000
+#define MXC_PLL_DP_CTL_ADE 0x800
+#define MXC_PLL_DP_CTL_REF_CLK_DIV 0x400
+#define MXC_PLL_DP_CTL_HFSM 0x80
+#define MXC_PLL_DP_CTL_PRE 0x40
+#define MXC_PLL_DP_CTL_UPEN 0x20
+#define MXC_PLL_DP_CTL_RST 0x10
+#define MXC_PLL_DP_CTL_RCP 0x8
+#define MXC_PLL_DP_CTL_PLM 0x4
+#define MXC_PLL_DP_CTL_BRM0 0x2
+#define MXC_PLL_DP_CTL_LRF 0x1
+
+#define MXC_PLL_DP_OP_MFI_OFFSET 4
+#define MXC_PLL_DP_OP_MFI_MASK 0xF
+#define MXC_PLL_DP_OP_PDF_OFFSET 0
+#define MXC_PLL_DP_OP_PDF_MASK 0xF
+
+#define MXC_PLL_DP_MFD_OFFSET 0
+#define MXC_PLL_DP_MFD_MASK 0x7FFFFFF
+
+#define MXC_PLL_DP_MFN_OFFSET 0
+#define MXC_PLL_DP_MFN_MASK 0x7FFFFFF
+
+/* CRM AP Register Offsets */
+#define MXC_CRMAP_ASCSR (MXC_CRM_AP_BASE + 0x00)
+#define MXC_CRMAP_ACDR (MXC_CRM_AP_BASE + 0x04)
+#define MXC_CRMAP_ACDER1 (MXC_CRM_AP_BASE + 0x08)
+#define MXC_CRMAP_ACDER2 (MXC_CRM_AP_BASE + 0x0C)
+#define MXC_CRMAP_ACGCR (MXC_CRM_AP_BASE + 0x10)
+#define MXC_CRMAP_ACCGCR (MXC_CRM_AP_BASE + 0x14)
+#define MXC_CRMAP_AMLPMRA (MXC_CRM_AP_BASE + 0x18)
+#define MXC_CRMAP_AMLPMRB (MXC_CRM_AP_BASE + 0x1C)
+#define MXC_CRMAP_AMLPMRC (MXC_CRM_AP_BASE + 0x20)
+#define MXC_CRMAP_AMLPMRD (MXC_CRM_AP_BASE + 0x24)
+#define MXC_CRMAP_AMLPMRE1 (MXC_CRM_AP_BASE + 0x28)
+#define MXC_CRMAP_AMLPMRE2 (MXC_CRM_AP_BASE + 0x2C)
+#define MXC_CRMAP_AMLPMRF (MXC_CRM_AP_BASE + 0x30)
+#define MXC_CRMAP_AMLPMRG (MXC_CRM_AP_BASE + 0x34)
+#define MXC_CRMAP_APGCR (MXC_CRM_AP_BASE + 0x38)
+#define MXC_CRMAP_ACSR (MXC_CRM_AP_BASE + 0x3C)
+#define MXC_CRMAP_ADCR (MXC_CRM_AP_BASE + 0x40)
+#define MXC_CRMAP_ACR (MXC_CRM_AP_BASE + 0x44)
+#define MXC_CRMAP_AMCR (MXC_CRM_AP_BASE + 0x48)
+#define MXC_CRMAP_APCR (MXC_CRM_AP_BASE + 0x4C)
+#define MXC_CRMAP_AMORA (MXC_CRM_AP_BASE + 0x50)
+#define MXC_CRMAP_AMORB (MXC_CRM_AP_BASE + 0x54)
+#define MXC_CRMAP_AGPR (MXC_CRM_AP_BASE + 0x58)
+#define MXC_CRMAP_APRA (MXC_CRM_AP_BASE + 0x5C)
+#define MXC_CRMAP_APRB (MXC_CRM_AP_BASE + 0x60)
+#define MXC_CRMAP_APOR (MXC_CRM_AP_BASE + 0x64)
+#define MXC_CRMAP_ADFMR (MXC_CRM_AP_BASE + 0x68)
+
+/* CRM AP Register Bit definitions */
+#define MXC_CRMAP_ASCSR_CRS 0x10000
+#define MXC_CRMAP_ASCSR_AP_PATDIV2_OFFSET 15
+#define MXC_CRMAP_ASCSR_AP_PATREF_DIV2 0x8000
+#define MXC_CRMAP_ASCSR_USBSEL_OFFSET 13
+#define MXC_CRMAP_ASCSR_USBSEL_MASK (0x3 << 13)
+#define MXC_CRMAP_ASCSR_CSISEL_OFFSET 11
+#define MXC_CRMAP_ASCSR_CSISEL_MASK (0x3 << 11)
+#define MXC_CRMAP_ASCSR_SSI2SEL_OFFSET 7
+#define MXC_CRMAP_ASCSR_SSI2SEL_MASK (0x3 << 7)
+#define MXC_CRMAP_ASCSR_SSI1SEL_OFFSET 5
+#define MXC_CRMAP_ASCSR_SSI1SEL_MASK (0x3 << 5)
+#define MXC_CRMAP_ASCSR_APSEL_OFFSET 3
+#define MXC_CRMAP_ASCSR_APSEL_MASK (0x3 << 3)
+#define MXC_CRMAP_ASCSR_AP_PATDIV1_OFFSET 2
+#define MXC_CRMAP_ASCSR_AP_PATREF_DIV1 0x4
+#define MXC_CRMAP_ASCSR_APISEL 0x1
+
+#define MXC_CRMAP_ACDR_ARMDIV_OFFSET 8
+#define MXC_CRMAP_ACDR_ARMDIV_MASK (0xF << 8)
+#define MXC_CRMAP_ACDR_AHBDIV_OFFSET 4
+#define MXC_CRMAP_ACDR_AHBDIV_MASK (0xF << 4)
+#define MXC_CRMAP_ACDR_IPDIV_OFFSET 0
+#define MXC_CRMAP_ACDR_IPDIV_MASK 0xF
+
+#define MXC_CRMAP_ACDER1_CSIEN_OFFSET 30
+#define MXC_CRMAP_ACDER1_CSIDIV_OFFSET 24
+#define MXC_CRMAP_ACDER1_CSIDIV_MASK (0x3F << 24)
+#define MXC_CRMAP_ACDER1_SSI2EN_OFFSET 14
+#define MXC_CRMAP_ACDER1_SSI2DIV_OFFSET 8
+#define MXC_CRMAP_ACDER1_SSI2DIV_MASK (0x3F << 8)
+#define MXC_CRMAP_ACDER1_SSI1EN_OFFSET 6
+#define MXC_CRMAP_ACDER1_SSI1DIV_OFFSET 0
+#define MXC_CRMAP_ACDER1_SSI1DIV_MASK 0x3F
+
+#define MXC_CRMAP_ACDER2_CRCT_CLK_DIV_OFFSET 24
+#define MXC_CRMAP_ACDER2_CRCT_CLK_DIV_MASK (0x7 << 24)
+#define MXC_CRMAP_ACDER2_NFCEN_OFFSET 20
+#define MXC_CRMAP_ACDER2_NFCDIV_OFFSET 16
+#define MXC_CRMAP_ACDER2_NFCDIV_MASK (0xF << 16)
+#define MXC_CRMAP_ACDER2_USBEN_OFFSET 12
+#define MXC_CRMAP_ACDER2_USBDIV_OFFSET 8
+#define MXC_CRMAP_ACDER2_USBDIV_MASK (0xF << 8)
+#define MXC_CRMAP_ACDER2_BAUD_ISEL_OFFSET 5
+#define MXC_CRMAP_ACDER2_BAUD_ISEL_MASK (0x3 << 5)
+#define MXC_CRMAP_ACDER2_BAUDDIV_OFFSET 0
+#define MXC_CRMAP_ACDER2_BAUDDIV_MASK 0xF
+
+#define MXC_CRMAP_AMLPMRA_MLPMA7_OFFSET 22
+#define MXC_CRMAP_AMLPMRA_MLPMA7_MASK (0x7 << 22)
+#define MXC_CRMAP_AMLPMRA_MLPMA6_OFFSET 19
+#define MXC_CRMAP_AMLPMRA_MLPMA6_MASK (0x7 << 19)
+#define MXC_CRMAP_AMLPMRA_MLPMA4_OFFSET 12
+#define MXC_CRMAP_AMLPMRA_MLPMA4_MASK (0x7 << 12)
+#define MXC_CRMAP_AMLPMRA_MLPMA3_OFFSET 9
+#define MXC_CRMAP_AMLPMRA_MLPMA3_MASK (0x7 << 9)
+#define MXC_CRMAP_AMLPMRA_MLPMA2_OFFSET 6
+#define MXC_CRMAP_AMLPMRA_MLPMA2_MASK (0x7 << 6)
+#define MXC_CRMAP_AMLPMRA_MLPMA1_OFFSET 3
+#define MXC_CRMAP_AMLPMRA_MLPMA1_MASK (0x7 << 3)
+
+#define MXC_CRMAP_AMLPMRB_MLPMB0_OFFSET 0
+#define MXC_CRMAP_AMLPMRB_MLPMB0_MASK 0x7
+
+#define MXC_CRMAP_AMLPMRC_MLPMC9_OFFSET 28
+#define MXC_CRMAP_AMLPMRC_MLPMC9_MASK (0x7 << 28)
+#define MXC_CRMAP_AMLPMRC_MLPMC7_OFFSET 22
+#define MXC_CRMAP_AMLPMRC_MLPMC7_MASK (0x7 << 22)
+#define MXC_CRMAP_AMLPMRC_MLPMC5_OFFSET 16
+#define MXC_CRMAP_AMLPMRC_MLPMC5_MASK (0x7 << 16)
+#define MXC_CRMAP_AMLPMRC_MLPMC4_OFFSET 12
+#define MXC_CRMAP_AMLPMRC_MLPMC4_MASK (0x7 << 12)
+#define MXC_CRMAP_AMLPMRC_MLPMC3_OFFSET 9
+#define MXC_CRMAP_AMLPMRC_MLPMC3_MASK (0x7 << 9)
+#define MXC_CRMAP_AMLPMRC_MLPMC2_OFFSET 6
+#define MXC_CRMAP_AMLPMRC_MLPMC2_MASK (0x7 << 6)
+#define MXC_CRMAP_AMLPMRC_MLPMC1_OFFSET 3
+#define MXC_CRMAP_AMLPMRC_MLPMC1_MASK (0x7 << 3)
+#define MXC_CRMAP_AMLPMRC_MLPMC0_OFFSET 0
+#define MXC_CRMAP_AMLPMRC_MLPMC0_MASK 0x7
+
+#define MXC_CRMAP_AMLPMRD_MLPMD7_OFFSET 22
+#define MXC_CRMAP_AMLPMRD_MLPMD7_MASK (0x7 << 22)
+#define MXC_CRMAP_AMLPMRD_MLPMD4_OFFSET 12
+#define MXC_CRMAP_AMLPMRD_MLPMD4_MASK (0x7 << 12)
+#define MXC_CRMAP_AMLPMRD_MLPMD3_OFFSET 9
+#define MXC_CRMAP_AMLPMRD_MLPMD3_MASK (0x7 << 9)
+#define MXC_CRMAP_AMLPMRD_MLPMD2_OFFSET 6
+#define MXC_CRMAP_AMLPMRD_MLPMD2_MASK (0x7 << 6)
+#define MXC_CRMAP_AMLPMRD_MLPMD0_OFFSET 0
+#define MXC_CRMAP_AMLPMRD_MLPMD0_MASK 0x7
+
+#define MXC_CRMAP_AMLPMRE1_MLPME9_OFFSET 28
+#define MXC_CRMAP_AMLPMRE1_MLPME9_MASK (0x7 << 28)
+#define MXC_CRMAP_AMLPMRE1_MLPME8_OFFSET 25
+#define MXC_CRMAP_AMLPMRE1_MLPME8_MASK (0x7 << 25)
+#define MXC_CRMAP_AMLPMRE1_MLPME7_OFFSET 22
+#define MXC_CRMAP_AMLPMRE1_MLPME7_MASK (0x7 << 22)
+#define MXC_CRMAP_AMLPMRE1_MLPME6_OFFSET 19
+#define MXC_CRMAP_AMLPMRE1_MLPME6_MASK (0x7 << 19)
+#define MXC_CRMAP_AMLPMRE1_MLPME5_OFFSET 16
+#define MXC_CRMAP_AMLPMRE1_MLPME5_MASK (0x7 << 16)
+#define MXC_CRMAP_AMLPMRE1_MLPME4_OFFSET 12
+#define MXC_CRMAP_AMLPMRE1_MLPME4_MASK (0x7 << 12)
+#define MXC_CRMAP_AMLPMRE1_MLPME3_OFFSET 9
+#define MXC_CRMAP_AMLPMRE1_MLPME3_MASK (0x7 << 9)
+#define MXC_CRMAP_AMLPMRE1_MLPME2_OFFSET 6
+#define MXC_CRMAP_AMLPMRE1_MLPME2_MASK (0x7 << 6)
+#define MXC_CRMAP_AMLPMRE1_MLPME1_OFFSET 3
+#define MXC_CRMAP_AMLPMRE1_MLPME1_MASK (0x7 << 3)
+#define MXC_CRMAP_AMLPMRE1_MLPME0_OFFSET 0
+#define MXC_CRMAP_AMLPMRE1_MLPME0_MASK 0x7
+
+#define MXC_CRMAP_AMLPMRE2_MLPME0_OFFSET 0
+#define MXC_CRMAP_AMLPMRE2_MLPME0_MASK 0x7
+
+#define MXC_CRMAP_AMLPMRF_MLPMF6_OFFSET 19
+#define MXC_CRMAP_AMLPMRF_MLPMF6_MASK (0x7 << 19)
+#define MXC_CRMAP_AMLPMRF_MLPMF5_OFFSET 16
+#define MXC_CRMAP_AMLPMRF_MLPMF5_MASK (0x7 << 16)
+#define MXC_CRMAP_AMLPMRF_MLPMF3_OFFSET 9
+#define MXC_CRMAP_AMLPMRF_MLPMF3_MASK (0x7 << 9)
+#define MXC_CRMAP_AMLPMRF_MLPMF2_OFFSET 6
+#define MXC_CRMAP_AMLPMRF_MLPMF2_MASK (0x7 << 6)
+#define MXC_CRMAP_AMLPMRF_MLPMF1_OFFSET 3
+#define MXC_CRMAP_AMLPMRF_MLPMF1_MASK (0x7 << 3)
+#define MXC_CRMAP_AMLPMRF_MLPMF0_OFFSET 0
+#define MXC_CRMAP_AMLPMRF_MLPMF0_MASK (0x7 << 0)
+
+#define MXC_CRMAP_AMLPMRG_MLPMG9_OFFSET 28
+#define MXC_CRMAP_AMLPMRG_MLPMG9_MASK (0x7 << 28)
+#define MXC_CRMAP_AMLPMRG_MLPMG7_OFFSET 22
+#define MXC_CRMAP_AMLPMRG_MLPMG7_MASK (0x7 << 22)
+#define MXC_CRMAP_AMLPMRG_MLPMG6_OFFSET 19
+#define MXC_CRMAP_AMLPMRG_MLPMG6_MASK (0x7 << 19)
+#define MXC_CRMAP_AMLPMRG_MLPMG5_OFFSET 16
+#define MXC_CRMAP_AMLPMRG_MLPMG5_MASK (0x7 << 16)
+#define MXC_CRMAP_AMLPMRG_MLPMG4_OFFSET 12
+#define MXC_CRMAP_AMLPMRG_MLPMG4_MASK (0x7 << 12)
+#define MXC_CRMAP_AMLPMRG_MLPMG3_OFFSET 9
+#define MXC_CRMAP_AMLPMRG_MLPMG3_MASK (0x7 << 9)
+#define MXC_CRMAP_AMLPMRG_MLPMG2_OFFSET 6
+#define MXC_CRMAP_AMLPMRG_MLPMG2_MASK (0x7 << 6)
+#define MXC_CRMAP_AMLPMRG_MLPMG1_OFFSET 3
+#define MXC_CRMAP_AMLPMRG_MLPMG1_MASK (0x7 << 3)
+#define MXC_CRMAP_AMLPMRG_MLPMG0_OFFSET 0
+#define MXC_CRMAP_AMLPMRG_MLPMG0_MASK 0x7
+
+#define MXC_CRMAP_AGPR_IPUPAD_OFFSET 20
+#define MXC_CRMAP_AGPR_IPUPAD_MASK (0x7 << 20)
+
+#define MXC_CRMAP_APRA_EL1TEN_OFFSET 29
+#define MXC_CRMAP_APRA_SIMEN_OFFSET 24
+#define MXC_CRMAP_APRA_UART3DIV_OFFSET 17
+#define MXC_CRMAP_APRA_UART3DIV_MASK (0xF << 17)
+#define MXC_CRMAP_APRA_UART3EN_OFFSET 16
+#define MXC_CRMAP_APRA_SAHARA_DIV2_CLKEN_OFFSET 14
+#define MXC_CRMAP_APRA_MQSPIEN_OFFSET 13
+#define MXC_CRMAP_APRA_UART2EN_OFFSET 8
+#define MXC_CRMAP_APRA_UART1EN_OFFSET 0
+
+#define MXC_CRMAP_APRB_SDHC2_ISEL_OFFSET 13
+#define MXC_CRMAP_APRB_SDHC2_ISEL_MASK (0x7 << 13)
+#define MXC_CRMAP_APRB_SDHC2_DIV_OFFSET 9
+#define MXC_CRMAP_APRB_SDHC2_DIV_MASK (0xF << 9)
+#define MXC_CRMAP_APRB_SDHC2EN_OFFSET 8
+#define MXC_CRMAP_APRB_SDHC1_ISEL_OFFSET 5
+#define MXC_CRMAP_APRB_SDHC1_ISEL_MASK (0x7 << 5)
+#define MXC_CRMAP_APRB_SDHC1_DIV_OFFSET 1
+#define MXC_CRMAP_APRB_SDHC1_DIV_MASK (0xF << 1)
+#define MXC_CRMAP_APRB_SDHC1EN_OFFSET 0
+
+#define MXC_CRMAP_ACSR_ADS_OFFSET 8
+#define MXC_CRMAP_ACSR_ADS (0x1 << 8)
+#define MXC_CRMAP_ACSR_ACS 0x1
+
+#define MXC_CRMAP_ADCR_LFDF_0 (0x0 << 8)
+#define MXC_CRMAP_ADCR_LFDF_2 (0x1 << 8)
+#define MXC_CRMAP_ADCR_LFDF_4 (0x2 << 8)
+#define MXC_CRMAP_ADCR_LFDF_8 (0x3 << 8)
+#define MXC_CRMAP_ADCR_LFDF_OFFSET 8
+#define MXC_CRMAP_ADCR_LFDF_MASK (0x3 << 8)
+#define MXC_CRMAP_ADCR_ALT_PLL 0x80
+#define MXC_CRMAP_ADCR_DFS_DIVEN 0x20
+#define MXC_CRMAP_ADCR_DIV_BYP 0x2
+#define MXC_CRMAP_ADCR_VSTAT 0x8
+#define MXC_CRMAP_ADCR_TSTAT 0x10
+#define MXC_CRMAP_ADCR_DVFS_VCTRL 0x10
+#define MXC_CRMAP_ADCR_CLK_ON 0x40
+
+#define MXC_CRMAP_ADFMR_FC_OFFSET 16
+#define MXC_CRMAP_ADFMR_FC_MASK (0x1F << 16)
+#define MXC_CRMAP_ADFMR_MF_OFFSET 1
+#define MXC_CRMAP_ADFMR_MF_MASK (0x3FF << 1)
+#define MXC_CRMAP_ADFMR_DFM_CLK_READY 0x1
+#define MXC_CRMAP_ADFMR_DFM_PWR_DOWN 0x8000
+
+#define MXC_CRMAP_ACR_CKOHS_HIGH (1 << 18)
+#define MXC_CRMAP_ACR_CKOS_HIGH (1 << 16)
+#define MXC_CRMAP_ACR_CKOHS_MASK (0x7 << 12)
+#define MXC_CRMAP_ACR_CKOHD (1 << 11)
+#define MXC_CRMAP_ACR_CKOHDIV_MASK (0xF << 8)
+#define MXC_CRMAP_ACR_CKOHDIV_OFFSET 8
+#define MXC_CRMAP_ACR_CKOD (1 << 7)
+#define MXC_CRMAP_ACR_CKOS_MASK (0x7 << 4)
+
+/* AP Warm reset */
+#define MXC_CRMAP_AMCR_SW_AP (1 << 14)
+
+/* Bit definitions of ACGCR in CRM_AP for tree level clock gating */
+#define MXC_CRMAP_ACGCR_ACG0_STOP_WAIT 0x00000001
+#define MXC_CRMAP_ACGCR_ACG0_STOP 0x00000003
+#define MXC_CRMAP_ACGCR_ACG0_RUN 0x00000007
+#define MXC_CRMAP_ACGCR_ACG0_DISABLED 0x00000000
+
+#define MXC_CRMAP_ACGCR_ACG1_STOP_WAIT 0x00000008
+#define MXC_CRMAP_ACGCR_ACG1_STOP 0x00000018
+#define MXC_CRMAP_ACGCR_ACG1_RUN 0x00000038
+#define MXC_CRMAP_ACGCR_ACG1_DISABLED 0x00000000
+
+#define MXC_CRMAP_ACGCR_ACG2_STOP_WAIT 0x00000040
+#define MXC_CRMAP_ACGCR_ACG2_STOP 0x000000C0
+#define MXC_CRMAP_ACGCR_ACG2_RUN 0x000001C0
+#define MXC_CRMAP_ACGCR_ACG2_DISABLED 0x00000000
+
+#define MXC_CRMAP_ACGCR_ACG3_STOP_WAIT 0x00000200
+#define MXC_CRMAP_ACGCR_ACG3_STOP 0x00000600
+#define MXC_CRMAP_ACGCR_ACG3_RUN 0x00000E00
+#define MXC_CRMAP_ACGCR_ACG3_DISABLED 0x00000000
+
+#define MXC_CRMAP_ACGCR_ACG4_STOP_WAIT 0x00001000
+#define MXC_CRMAP_ACGCR_ACG4_STOP 0x00003000
+#define MXC_CRMAP_ACGCR_ACG4_RUN 0x00007000
+#define MXC_CRMAP_ACGCR_ACG4_DISABLED 0x00000000
+
+#define MXC_CRMAP_ACGCR_ACG5_STOP_WAIT 0x00010000
+#define MXC_CRMAP_ACGCR_ACG5_STOP 0x00030000
+#define MXC_CRMAP_ACGCR_ACG5_RUN 0x00070000
+#define MXC_CRMAP_ACGCR_ACG5_DISABLED 0x00000000
+
+#define MXC_CRMAP_ACGCR_ACG6_STOP_WAIT 0x00080000
+#define MXC_CRMAP_ACGCR_ACG6_STOP 0x00180000
+#define MXC_CRMAP_ACGCR_ACG6_RUN 0x00380000
+#define MXC_CRMAP_ACGCR_ACG6_DISABLED 0x00000000
+
+#define NUM_GATE_CTRL 6
+
+/* CRM COM Register Offsets */
+#define MXC_CRMCOM_CSCR (MXC_CRM_COM_BASE + 0x0C)
+#define MXC_CRMCOM_CCCR (MXC_CRM_COM_BASE + 0x10)
+
+/* CRM COM Bit Definitions */
+#define MXC_CRMCOM_CSCR_PPD1 0x08000000
+#define MXC_CRMCOM_CSCR_CKOHSEL (1 << 18)
+#define MXC_CRMCOM_CSCR_CKOSEL (1 << 17)
+#define MXC_CRMCOM_CCCR_CC_DIV_OFFSET 8
+#define MXC_CRMCOM_CCCR_CC_DIV_MASK (0x1F << 8)
+#define MXC_CRMCOM_CCCR_CC_SEL_OFFSET 0
+#define MXC_CRMCOM_CCCR_CC_SEL_MASK 0x3
+
+/* DSM Register Offsets */
+#define MXC_DSM_SLEEP_TIME (MXC_DSM_BASE + 0x0c)
+#define MXC_DSM_CONTROL0 (MXC_DSM_BASE + 0x20)
+#define MXC_DSM_CONTROL1 (MXC_DSM_BASE + 0x24)
+#define MXC_DSM_CTREN (MXC_DSM_BASE + 0x28)
+#define MXC_DSM_WARM_PER (MXC_DSM_BASE + 0x40)
+#define MXC_DSM_LOCK_PER (MXC_DSM_BASE + 0x44)
+#define MXC_DSM_MGPER (MXC_DSM_BASE + 0x4c)
+#define MXC_DSM_CRM_CONTROL (MXC_DSM_BASE + 0x50)
+
+/* Bit definitions of various registers in DSM */
+#define MXC_DSM_CRM_CTRL_DVFS_BYP 0x00000008
+#define MXC_DSM_CRM_CTRL_DVFS_VCTRL 0x00000004
+#define MXC_DSM_CRM_CTRL_LPMD1 0x00000002
+#define MXC_DSM_CRM_CTRL_LPMD0 0x00000001
+#define MXC_DSM_CRM_CTRL_LPMD_STOP_MODE 0x00000000
+#define MXC_DSM_CRM_CTRL_LPMD_WAIT_MODE 0x00000001
+#define MXC_DSM_CRM_CTRL_LPMD_RUN_MODE 0x00000003
+#define MXC_DSM_CONTROL0_STBY_COMMIT_EN 0x00000200
+#define MXC_DSM_CONTROL0_MSTR_EN 0x00000001
+#define MXC_DSM_CONTROL0_RESTART 0x00000010
+/* Counter Block reset */
+#define MXC_DSM_CONTROL1_CB_RST 0x00000002
+/* State Machine reset */
+#define MXC_DSM_CONTROL1_SM_RST 0x00000004
+/* Bit needed to reset counter block */
+#define MXC_CONTROL1_RST_CNT32 0x00000008
+#define MXC_DSM_CONTROL1_RST_CNT32_EN 0x00000800
+#define MXC_DSM_CONTROL1_SLEEP 0x00000100
+#define MXC_DSM_CONTROL1_WAKEUP_DISABLE 0x00004000
+#define MXC_DSM_CTREN_CNT32 0x00000001
+
+/* Magic Fix enable bit */
+#define MXC_DSM_MGPER_EN_MGFX 0x80000000
+#define MXC_DSM_MGPER_PER_MASK 0x000003FF
+#define MXC_DSM_MGPER_PER(n) (MXC_DSM_MGPER_PER_MASK & n)
+
+/* Address offsets of the CLKCTL registers */
+#define MXC_CLKCTL_GP_CTRL (MXC_CLKCTL_BASE + 0x00)
+#define MXC_CLKCTL_GP_SER (MXC_CLKCTL_BASE + 0x04)
+#define MXC_CLKCTL_GP_CER (MXC_CLKCTL_BASE + 0x08)
+
+#endif /* _ARCH_ARM_MACH_MXC91231_CRM_REGS_H_ */
diff --git a/arch/arm/mach-mxc91231/devices.c b/arch/arm/mach-mxc91231/devices.c
new file mode 100644
index 00000000000..353bd977b39
--- /dev/null
+++ b/arch/arm/mach-mxc91231/devices.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2006-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Sascha Hauer, kernel@pengutronix.de
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serial.h>
+#include <linux/gpio.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/imx-uart.h>
+
+static struct resource uart0[] = {
+ {
+ .start = MXC91231_UART1_BASE_ADDR,
+ .end = MXC91231_UART1_BASE_ADDR + 0x0B5,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC91231_INT_UART1_RX,
+ .end = MXC91231_INT_UART1_RX,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = MXC91231_INT_UART1_TX,
+ .end = MXC91231_INT_UART1_TX,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = MXC91231_INT_UART1_MINT,
+ .end = MXC91231_INT_UART1_MINT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_uart_device0 = {
+ .name = "imx-uart",
+ .id = 0,
+ .resource = uart0,
+ .num_resources = ARRAY_SIZE(uart0),
+};
+
+static struct resource uart1[] = {
+ {
+ .start = MXC91231_UART2_BASE_ADDR,
+ .end = MXC91231_UART2_BASE_ADDR + 0x0B5,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC91231_INT_UART2_RX,
+ .end = MXC91231_INT_UART2_RX,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = MXC91231_INT_UART2_TX,
+ .end = MXC91231_INT_UART2_TX,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = MXC91231_INT_UART2_MINT,
+ .end = MXC91231_INT_UART2_MINT,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_uart_device1 = {
+ .name = "imx-uart",
+ .id = 1,
+ .resource = uart1,
+ .num_resources = ARRAY_SIZE(uart1),
+};
+
+static struct resource uart2[] = {
+ {
+ .start = MXC91231_UART3_BASE_ADDR,
+ .end = MXC91231_UART3_BASE_ADDR + 0x0B5,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC91231_INT_UART3_RX,
+ .end = MXC91231_INT_UART3_RX,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = MXC91231_INT_UART3_TX,
+ .end = MXC91231_INT_UART3_TX,
+ .flags = IORESOURCE_IRQ,
+ }, {
+ .start = MXC91231_INT_UART3_MINT,
+ .end = MXC91231_INT_UART3_MINT,
+ .flags = IORESOURCE_IRQ,
+
+ },
+};
+
+struct platform_device mxc_uart_device2 = {
+ .name = "imx-uart",
+ .id = 2,
+ .resource = uart2,
+ .num_resources = ARRAY_SIZE(uart2),
+};
+
+/* GPIO port description */
+static struct mxc_gpio_port mxc_gpio_ports[] = {
+ [0] = {
+ .chip.label = "gpio-0",
+ .base = MXC91231_IO_ADDRESS(MXC91231_GPIO1_AP_BASE_ADDR),
+ .irq = MXC91231_INT_GPIO1,
+ .virtual_irq_start = MXC_GPIO_IRQ_START,
+ },
+ [1] = {
+ .chip.label = "gpio-1",
+ .base = MXC91231_IO_ADDRESS(MXC91231_GPIO2_AP_BASE_ADDR),
+ .irq = MXC91231_INT_GPIO2,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 32,
+ },
+ [2] = {
+ .chip.label = "gpio-2",
+ .base = MXC91231_IO_ADDRESS(MXC91231_GPIO3_AP_BASE_ADDR),
+ .irq = MXC91231_INT_GPIO3,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 64,
+ },
+ [3] = {
+ .chip.label = "gpio-3",
+ .base = MXC91231_IO_ADDRESS(MXC91231_GPIO4_SH_BASE_ADDR),
+ .irq = MXC91231_INT_GPIO4,
+ .virtual_irq_start = MXC_GPIO_IRQ_START + 96,
+ },
+};
+
+int __init mxc_register_gpios(void)
+{
+ return mxc_gpio_init(mxc_gpio_ports, ARRAY_SIZE(mxc_gpio_ports));
+}
+
+static struct resource mxc_nand_resources[] = {
+ {
+ .start = MXC91231_NFC_BASE_ADDR,
+ .end = MXC91231_NFC_BASE_ADDR + 0xfff,
+ .flags = IORESOURCE_MEM
+ }, {
+ .start = MXC91231_INT_NANDFC,
+ .end = MXC91231_INT_NANDFC,
+ .flags = IORESOURCE_IRQ
+ },
+};
+
+struct platform_device mxc_nand_device = {
+ .name = "mxc_nand",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_nand_resources),
+ .resource = mxc_nand_resources,
+};
+
+static struct resource mxc_sdhc0_resources[] = {
+ {
+ .start = MXC91231_MMC_SDHC1_BASE_ADDR,
+ .end = MXC91231_MMC_SDHC1_BASE_ADDR + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC91231_INT_MMC_SDHC1,
+ .end = MXC91231_INT_MMC_SDHC1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource mxc_sdhc1_resources[] = {
+ {
+ .start = MXC91231_MMC_SDHC2_BASE_ADDR,
+ .end = MXC91231_MMC_SDHC2_BASE_ADDR + SZ_16K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC91231_INT_MMC_SDHC2,
+ .end = MXC91231_INT_MMC_SDHC2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_sdhc_device0 = {
+ .name = "mxc-mmc",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_sdhc0_resources),
+ .resource = mxc_sdhc0_resources,
+};
+
+struct platform_device mxc_sdhc_device1 = {
+ .name = "mxc-mmc",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(mxc_sdhc1_resources),
+ .resource = mxc_sdhc1_resources,
+};
+
+static struct resource mxc_cspi0_resources[] = {
+ {
+ .start = MXC91231_CSPI1_BASE_ADDR,
+ .end = MXC91231_CSPI1_BASE_ADDR + 0x20,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC91231_INT_CSPI1,
+ .end = MXC91231_INT_CSPI1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_cspi_device0 = {
+ .name = "spi_imx",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_cspi0_resources),
+ .resource = mxc_cspi0_resources,
+};
+
+static struct resource mxc_cspi1_resources[] = {
+ {
+ .start = MXC91231_CSPI2_BASE_ADDR,
+ .end = MXC91231_CSPI2_BASE_ADDR + 0x20,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = MXC91231_INT_CSPI2,
+ .end = MXC91231_INT_CSPI2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device mxc_cspi_device1 = {
+ .name = "spi_imx",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(mxc_cspi1_resources),
+ .resource = mxc_cspi1_resources,
+};
+
+static struct resource mxc_wdog0_resources[] = {
+ {
+ .start = MXC91231_WDOG1_BASE_ADDR,
+ .end = MXC91231_WDOG1_BASE_ADDR + 0x10,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device mxc_wdog_device0 = {
+ .name = "mxc-wdt",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mxc_wdog0_resources),
+ .resource = mxc_wdog0_resources,
+};
diff --git a/arch/arm/mach-mxc91231/devices.h b/arch/arm/mach-mxc91231/devices.h
new file mode 100644
index 00000000000..72a2136ce27
--- /dev/null
+++ b/arch/arm/mach-mxc91231/devices.h
@@ -0,0 +1,13 @@
+extern struct platform_device mxc_uart_device0;
+extern struct platform_device mxc_uart_device1;
+extern struct platform_device mxc_uart_device2;
+
+extern struct platform_device mxc_nand_device;
+
+extern struct platform_device mxc_sdhc_device0;
+extern struct platform_device mxc_sdhc_device1;
+
+extern struct platform_device mxc_cspi_device0;
+extern struct platform_device mxc_cspi_device1;
+
+extern struct platform_device mxc_wdog_device0;
diff --git a/arch/arm/mach-mxc91231/iomux.c b/arch/arm/mach-mxc91231/iomux.c
new file mode 100644
index 00000000000..405d9b19d89
--- /dev/null
+++ b/arch/arm/mach-mxc91231/iomux.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2008 by Sascha Hauer <kernel@pengutronix.de>
+ * Copyright (C) 2009 by Valentin Longchamp <valentin.longchamp@epfl.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+#include <mach/iomux-mxc91231.h>
+
+/*
+ * IOMUX register (base) addresses
+ */
+#define IOMUX_AP_BASE MXC91231_IO_ADDRESS(MXC91231_IOMUX_AP_BASE_ADDR)
+#define IOMUX_COM_BASE MXC91231_IO_ADDRESS(MXC91231_IOMUX_COM_BASE_ADDR)
+#define IOMUXSW_AP_MUX_CTL (IOMUX_AP_BASE + 0x000)
+#define IOMUXSW_SP_MUX_CTL (IOMUX_COM_BASE + 0x000)
+#define IOMUXSW_PAD_CTL (IOMUX_COM_BASE + 0x200)
+
+#define IOMUXINT_OBS1 (IOMUX_AP_BASE + 0x600)
+#define IOMUXINT_OBS2 (IOMUX_AP_BASE + 0x004)
+
+static DEFINE_SPINLOCK(gpio_mux_lock);
+
+#define NB_PORTS ((PIN_MAX + 32) / 32)
+#define PIN_GLOBAL_NUM(pin) \
+ (((pin & MUX_SIDE_MASK) >> MUX_SIDE_SHIFT)*PIN_AP_MAX + \
+ ((pin & MUX_REG_MASK) >> MUX_REG_SHIFT)*4 + \
+ ((pin & MUX_FIELD_MASK) >> MUX_FIELD_SHIFT))
+
+unsigned long mxc_pin_alloc_map[NB_PORTS * 32 / BITS_PER_LONG];
+/*
+ * set the mode for a IOMUX pin.
+ */
+int mxc_iomux_mode(const unsigned int pin_mode)
+{
+ u32 side, field, l, mode, ret = 0;
+ void __iomem *reg;
+
+ side = (pin_mode & MUX_SIDE_MASK) >> MUX_SIDE_SHIFT;
+ switch (side) {
+ case MUX_SIDE_AP:
+ reg = IOMUXSW_AP_MUX_CTL;
+ break;
+ case MUX_SIDE_SP:
+ reg = IOMUXSW_SP_MUX_CTL;
+ break;
+ default:
+ return -EINVAL;
+ }
+ reg += ((pin_mode & MUX_REG_MASK) >> MUX_REG_SHIFT) * 4;
+ field = (pin_mode & MUX_FIELD_MASK) >> MUX_FIELD_SHIFT;
+ mode = (pin_mode & MUX_MODE_MASK) >> MUX_MODE_SHIFT;
+
+ spin_lock(&gpio_mux_lock);
+
+ l = __raw_readl(reg);
+ l &= ~(0xff << (field * 8));
+ l |= mode << (field * 8);
+ __raw_writel(l, reg);
+
+ spin_unlock(&gpio_mux_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(mxc_iomux_mode);
+
+/*
+ * This function configures the pad value for a IOMUX pin.
+ */
+void mxc_iomux_set_pad(enum iomux_pins pin, u32 config)
+{
+ u32 padgrp, field, l;
+ void __iomem *reg;
+
+ padgrp = (pin & MUX_PADGRP_MASK) >> MUX_PADGRP_SHIFT;
+ reg = IOMUXSW_PAD_CTL + (pin + 2) / 3 * 4;
+ field = (pin + 2) % 3;
+
+ pr_debug("%s: reg offset = 0x%x, field = %d\n",
+ __func__, (pin + 2) / 3, field);
+
+ spin_lock(&gpio_mux_lock);
+
+ l = __raw_readl(reg);
+ l &= ~(0x1ff << (field * 10));
+ l |= config << (field * 10);
+ __raw_writel(l, reg);
+
+ spin_unlock(&gpio_mux_lock);
+}
+EXPORT_SYMBOL(mxc_iomux_set_pad);
+
+/*
+ * allocs a single pin:
+ * - reserves the pin so that it is not claimed by another driver
+ * - setups the iomux according to the configuration
+ */
+int mxc_iomux_alloc_pin(const unsigned int pin_mode, const char *label)
+{
+ unsigned pad = PIN_GLOBAL_NUM(pin_mode);
+ if (pad >= (PIN_MAX + 1)) {
+ printk(KERN_ERR "mxc_iomux: Attempt to request nonexistant pin %u for \"%s\"\n",
+ pad, label ? label : "?");
+ return -EINVAL;
+ }
+
+ if (test_and_set_bit(pad, mxc_pin_alloc_map)) {
+ printk(KERN_ERR "mxc_iomux: pin %u already used. Allocation for \"%s\" failed\n",
+ pad, label ? label : "?");
+ return -EBUSY;
+ }
+ mxc_iomux_mode(pin_mode);
+
+ return 0;
+}
+EXPORT_SYMBOL(mxc_iomux_alloc_pin);
+
+int mxc_iomux_setup_multiple_pins(unsigned int *pin_list, unsigned count,
+ const char *label)
+{
+ unsigned int *p = pin_list;
+ int i;
+ int ret = -EINVAL;
+
+ for (i = 0; i < count; i++) {
+ ret = mxc_iomux_alloc_pin(*p, label);
+ if (ret)
+ goto setup_error;
+ p++;
+ }
+ return 0;
+
+setup_error:
+ mxc_iomux_release_multiple_pins(pin_list, i);
+ return ret;
+}
+EXPORT_SYMBOL(mxc_iomux_setup_multiple_pins);
+
+void mxc_iomux_release_pin(const unsigned int pin_mode)
+{
+ unsigned pad = PIN_GLOBAL_NUM(pin_mode);
+
+ if (pad < (PIN_MAX + 1))
+ clear_bit(pad, mxc_pin_alloc_map);
+}
+EXPORT_SYMBOL(mxc_iomux_release_pin);
+
+void mxc_iomux_release_multiple_pins(unsigned int *pin_list, int count)
+{
+ unsigned int *p = pin_list;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ mxc_iomux_release_pin(*p);
+ p++;
+ }
+}
+EXPORT_SYMBOL(mxc_iomux_release_multiple_pins);
diff --git a/arch/arm/mach-mxc91231/magx-zn5.c b/arch/arm/mach-mxc91231/magx-zn5.c
new file mode 100644
index 00000000000..7dbe4ca12ef
--- /dev/null
+++ b/arch/arm/mach-mxc91231/magx-zn5.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2009 Dmitriy Taychenachev <dimichxp@gmail.com>
+ *
+ * This file is released under the GPLv2 or later.
+ */
+
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/device.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/time.h>
+#include <asm/mach/arch.h>
+
+#include <mach/common.h>
+#include <mach/hardware.h>
+#include <mach/iomux-mxc91231.h>
+#include <mach/mmc.h>
+#include <mach/imx-uart.h>
+
+#include "devices.h"
+
+static struct imxuart_platform_data uart_pdata = {
+};
+
+static struct imxmmc_platform_data sdhc_pdata = {
+};
+
+static void __init zn5_init(void)
+{
+ pm_power_off = mxc91231_power_off;
+
+ mxc_iomux_alloc_pin(MXC91231_PIN_SP_USB_DAT_VP__RXD2, "uart2-rx");
+ mxc_iomux_alloc_pin(MXC91231_PIN_SP_USB_SE0_VM__TXD2, "uart2-tx");
+
+ mxc_register_device(&mxc_uart_device1, &uart_pdata);
+ mxc_register_device(&mxc_uart_device0, &uart_pdata);
+
+ mxc_register_device(&mxc_sdhc_device0, &sdhc_pdata);
+
+ mxc_register_device(&mxc_wdog_device0, NULL);
+
+ return;
+}
+
+static void __init zn5_timer_init(void)
+{
+ mxc91231_clocks_init(26000000); /* 26mhz ckih */
+}
+
+struct sys_timer zn5_timer = {
+ .init = zn5_timer_init,
+};
+
+MACHINE_START(MAGX_ZN5, "Motorola Zn5")
+ .phys_io = MXC91231_AIPS1_BASE_ADDR,
+ .io_pg_offst = ((MXC91231_AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
+ .boot_params = PHYS_OFFSET + 0x100,
+ .map_io = mxc91231_map_io,
+ .init_irq = mxc91231_init_irq,
+ .timer = &zn5_timer,
+ .init_machine = zn5_init,
+MACHINE_END
diff --git a/arch/arm/mach-mxc91231/mm.c b/arch/arm/mach-mxc91231/mm.c
new file mode 100644
index 00000000000..6becda3ff33
--- /dev/null
+++ b/arch/arm/mach-mxc91231/mm.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 1999,2000 Arm Limited
+ * Copyright (C) 2000 Deep Blue Solutions Ltd
+ * Copyright (C) 2002 Shane Nay (shane@minirl.com)
+ * Copyright 2004-2005 Freescale Semiconductor, Inc. All Rights Reserved.
+ * - add MXC specific definitions
+ * Copyright 2006 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <mach/common.h>
+#include <asm/pgtable.h>
+#include <asm/mach/map.h>
+
+/*
+ * This structure defines the MXC memory map.
+ */
+static struct map_desc mxc_io_desc[] __initdata = {
+ {
+ .virtual = MXC91231_L2CC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(MXC91231_L2CC_BASE_ADDR),
+ .length = MXC91231_L2CC_SIZE,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = MXC91231_X_MEMC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(MXC91231_X_MEMC_BASE_ADDR),
+ .length = MXC91231_X_MEMC_SIZE,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = MXC91231_ROMP_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(MXC91231_ROMP_BASE_ADDR),
+ .length = MXC91231_ROMP_SIZE,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = MXC91231_AVIC_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(MXC91231_AVIC_BASE_ADDR),
+ .length = MXC91231_AVIC_SIZE,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = MXC91231_AIPS1_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(MXC91231_AIPS1_BASE_ADDR),
+ .length = MXC91231_AIPS1_SIZE,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = MXC91231_SPBA0_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(MXC91231_SPBA0_BASE_ADDR),
+ .length = MXC91231_SPBA0_SIZE,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = MXC91231_SPBA1_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(MXC91231_SPBA1_BASE_ADDR),
+ .length = MXC91231_SPBA1_SIZE,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = MXC91231_AIPS2_BASE_ADDR_VIRT,
+ .pfn = __phys_to_pfn(MXC91231_AIPS2_BASE_ADDR),
+ .length = MXC91231_AIPS2_SIZE,
+ .type = MT_DEVICE,
+ },
+};
+
+/*
+ * This function initializes the memory map. It is called during the
+ * system startup to create static physical to virtual memory map for
+ * the IO modules.
+ */
+void __init mxc91231_map_io(void)
+{
+ mxc_set_cpu_type(MXC_CPU_MXC91231);
+
+ iotable_init(mxc_io_desc, ARRAY_SIZE(mxc_io_desc));
+}
+
+void __init mxc91231_init_irq(void)
+{
+ mxc_init_irq(MXC91231_IO_ADDRESS(MXC91231_AVIC_BASE_ADDR));
+}
diff --git a/arch/arm/mach-mxc91231/system.c b/arch/arm/mach-mxc91231/system.c
new file mode 100644
index 00000000000..736f7efd874
--- /dev/null
+++ b/arch/arm/mach-mxc91231/system.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2009 Dmitriy Taychenachev <dimichxp@gmail.com>
+ *
+ * This file is released under the GPLv2 or later.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <asm/proc-fns.h>
+#include <mach/hardware.h>
+
+#include "crm_regs.h"
+
+#define WDOG_WCR MXC91231_IO_ADDRESS(MXC91231_WDOG1_BASE_ADDR)
+#define WDOG_WCR_OUT_ENABLE (1 << 6)
+#define WDOG_WCR_ASSERT (1 << 5)
+
+void mxc91231_power_off(void)
+{
+ u16 wcr;
+
+ wcr = __raw_readw(WDOG_WCR);
+ wcr |= WDOG_WCR_OUT_ENABLE;
+ wcr &= ~WDOG_WCR_ASSERT;
+ __raw_writew(wcr, WDOG_WCR);
+}
+
+void mxc91231_arch_reset(char mode, const char *cmd)
+{
+ u32 amcr;
+
+ /* Reset the AP using CRM */
+ amcr = __raw_readl(MXC_CRMAP_AMCR);
+ amcr &= ~MXC_CRMAP_AMCR_SW_AP;
+ __raw_writel(amcr, MXC_CRMAP_AMCR);
+
+ mdelay(10);
+ cpu_reset(0);
+}
+
+void mxc91231_prepare_idle(void)
+{
+ u32 crm_ctl;
+
+ /* Go to WAIT mode after WFI */
+ crm_ctl = __raw_readl(MXC_DSM_CRM_CONTROL);
+ crm_ctl &= ~(MXC_DSM_CRM_CTRL_LPMD0 | MXC_DSM_CRM_CTRL_LPMD1);
+ crm_ctl |= MXC_DSM_CRM_CTRL_LPMD_WAIT_MODE;
+ __raw_writel(crm_ctl, MXC_DSM_CRM_CONTROL);
+}
diff --git a/arch/arm/mach-netx/include/mach/entry-macro.S b/arch/arm/mach-netx/include/mach/entry-macro.S
index a1952a0feda..844f1f9acbd 100644
--- a/arch/arm/mach-netx/include/mach/entry-macro.S
+++ b/arch/arm/mach-netx/include/mach/entry-macro.S
@@ -24,15 +24,13 @@
.endm
.macro get_irqnr_preamble, base, tmp
+ ldr \base, =io_p2v(0x001ff000)
.endm
.macro arch_ret_to_user, tmp1, tmp2
.endm
.macro get_irqnr_and_base, irqnr, irqstat, base, tmp
- mov \base, #io_p2v(0x00100000)
- add \base, \base, #0x000ff000
-
ldr \irqstat, [\base, #0]
clz \irqnr, \irqstat
rsb \irqnr, \irqnr, #31
diff --git a/arch/arm/mach-nomadik/Kconfig b/arch/arm/mach-nomadik/Kconfig
new file mode 100644
index 00000000000..2a02b49c40f
--- /dev/null
+++ b/arch/arm/mach-nomadik/Kconfig
@@ -0,0 +1,21 @@
+if ARCH_NOMADIK
+
+menu "Nomadik boards"
+
+config MACH_NOMADIK_8815NHK
+ bool "ST 8815 Nomadik Hardware Kit (evaluation board)"
+ select NOMADIK_8815
+
+endmenu
+
+config NOMADIK_8815
+ bool
+
+
+config I2C_BITBANG_8815NHK
+ tristate "Driver for bit-bang busses found on the 8815 NHK"
+ depends on I2C && MACH_NOMADIK_8815NHK
+ select I2C_ALGOBIT
+ default y
+
+endif
diff --git a/arch/arm/mach-nomadik/Makefile b/arch/arm/mach-nomadik/Makefile
new file mode 100644
index 00000000000..412040982a4
--- /dev/null
+++ b/arch/arm/mach-nomadik/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for the linux kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+
+# Object file lists.
+
+obj-y += clock.o timer.o gpio.o
+
+# Cpu revision
+obj-$(CONFIG_NOMADIK_8815) += cpu-8815.o
+
+# Specific board support
+obj-$(CONFIG_MACH_NOMADIK_8815NHK) += board-nhk8815.o
+
+# Nomadik extra devices
+obj-$(CONFIG_I2C_BITBANG_8815NHK) += i2c-8815nhk.o
diff --git a/arch/arm/mach-nomadik/Makefile.boot b/arch/arm/mach-nomadik/Makefile.boot
new file mode 100644
index 00000000000..c7e75acfe6c
--- /dev/null
+++ b/arch/arm/mach-nomadik/Makefile.boot
@@ -0,0 +1,4 @@
+ zreladdr-y := 0x00008000
+params_phys-y := 0x00000100
+initrd_phys-y := 0x00800000
+
diff --git a/arch/arm/mach-nomadik/board-nhk8815.c b/arch/arm/mach-nomadik/board-nhk8815.c
new file mode 100644
index 00000000000..79bdea943eb
--- /dev/null
+++ b/arch/arm/mach-nomadik/board-nhk8815.c
@@ -0,0 +1,111 @@
+/*
+ * linux/arch/arm/mach-nomadik/board-8815nhk.c
+ *
+ * Copyright (C) STMicroelectronics
+ *
+ * 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.
+ *
+ * NHK15 board specifc driver definition
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/amba/bus.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+#include <mach/setup.h>
+#include "clock.h"
+
+#define __MEM_4K_RESOURCE(x) \
+ .res = {.start = (x), .end = (x) + SZ_4K - 1, .flags = IORESOURCE_MEM}
+
+static struct amba_device uart0_device = {
+ .dev = { .init_name = "uart0" },
+ __MEM_4K_RESOURCE(NOMADIK_UART0_BASE),
+ .irq = {IRQ_UART0, NO_IRQ},
+};
+
+static struct amba_device uart1_device = {
+ .dev = { .init_name = "uart1" },
+ __MEM_4K_RESOURCE(NOMADIK_UART1_BASE),
+ .irq = {IRQ_UART1, NO_IRQ},
+};
+
+static struct amba_device *amba_devs[] __initdata = {
+ &uart0_device,
+ &uart1_device,
+};
+
+/* We have a fixed clock alone, by now */
+static struct clk nhk8815_clk_48 = {
+ .rate = 48*1000*1000,
+};
+
+static struct resource nhk8815_eth_resources[] = {
+ {
+ .name = "smc91x-regs",
+ .start = 0x34000000 + 0x300,
+ .end = 0x34000000 + SZ_64K - 1,
+ .flags = IORESOURCE_MEM,
+ }, {
+ .start = NOMADIK_GPIO_TO_IRQ(115),
+ .end = NOMADIK_GPIO_TO_IRQ(115),
+ .flags = IORESOURCE_IRQ | IRQF_TRIGGER_RISING,
+ }
+};
+
+static struct platform_device nhk8815_eth_device = {
+ .name = "smc91x",
+ .resource = nhk8815_eth_resources,
+ .num_resources = ARRAY_SIZE(nhk8815_eth_resources),
+};
+
+static int __init nhk8815_eth_init(void)
+{
+ int gpio_nr = 115; /* hardwired in the board */
+ int err;
+
+ err = gpio_request(gpio_nr, "eth_irq");
+ if (!err) err = nmk_gpio_set_mode(gpio_nr, NMK_GPIO_ALT_GPIO);
+ if (!err) err = gpio_direction_input(gpio_nr);
+ if (err)
+ pr_err("Error %i in %s\n", err, __func__);
+ return err;
+}
+device_initcall(nhk8815_eth_init);
+
+static struct platform_device *nhk8815_platform_devices[] __initdata = {
+ &nhk8815_eth_device,
+ /* will add more devices */
+};
+
+static void __init nhk8815_platform_init(void)
+{
+ int i;
+
+ cpu8815_platform_init();
+ platform_add_devices(nhk8815_platform_devices,
+ ARRAY_SIZE(nhk8815_platform_devices));
+
+ for (i = 0; i < ARRAY_SIZE(amba_devs); i++) {
+ nmdk_clk_create(&nhk8815_clk_48, amba_devs[i]->dev.init_name);
+ amba_device_register(amba_devs[i], &iomem_resource);
+ }
+}
+
+MACHINE_START(NOMADIK, "NHK8815")
+ /* Maintainer: ST MicroElectronics */
+ .phys_io = NOMADIK_UART0_BASE,
+ .io_pg_offst = (IO_ADDRESS(NOMADIK_UART0_BASE) >> 18) & 0xfffc,
+ .boot_params = 0x100,
+ .map_io = cpu8815_map_io,
+ .init_irq = cpu8815_init_irq,
+ .timer = &nomadik_timer,
+ .init_machine = nhk8815_platform_init,
+MACHINE_END
diff --git a/arch/arm/mach-nomadik/clock.c b/arch/arm/mach-nomadik/clock.c
new file mode 100644
index 00000000000..9f92502a008
--- /dev/null
+++ b/arch/arm/mach-nomadik/clock.c
@@ -0,0 +1,45 @@
+/*
+ * linux/arch/arm/mach-nomadik/clock.c
+ *
+ * Copyright (C) 2009 Alessandro Rubini
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <asm/clkdev.h>
+#include "clock.h"
+
+/*
+ * The nomadik board uses generic clocks, but the serial pl011 file
+ * calls clk_enable(), clk_disable(), clk_get_rate(), so we provide them
+ */
+unsigned long clk_get_rate(struct clk *clk)
+{
+ return clk->rate;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+/* enable and disable do nothing */
+int clk_enable(struct clk *clk)
+{
+ return 0;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+}
+EXPORT_SYMBOL(clk_disable);
+
+/* Create a clock structure with the given name */
+int nmdk_clk_create(struct clk *clk, const char *dev_id)
+{
+ struct clk_lookup *clkdev;
+
+ clkdev = clkdev_alloc(clk, NULL, dev_id);
+ if (!clkdev)
+ return -ENOMEM;
+ clkdev_add(clkdev);
+ return 0;
+}
diff --git a/arch/arm/mach-nomadik/clock.h b/arch/arm/mach-nomadik/clock.h
new file mode 100644
index 00000000000..235faec7f62
--- /dev/null
+++ b/arch/arm/mach-nomadik/clock.h
@@ -0,0 +1,14 @@
+
+/*
+ * linux/arch/arm/mach-nomadik/clock.h
+ *
+ * Copyright (C) 2009 Alessandro Rubini
+ *
+ * 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.
+ */
+struct clk {
+ unsigned long rate;
+};
+extern int nmdk_clk_create(struct clk *clk, const char *dev_id);
diff --git a/arch/arm/mach-nomadik/cpu-8815.c b/arch/arm/mach-nomadik/cpu-8815.c
new file mode 100644
index 00000000000..f93c5963419
--- /dev/null
+++ b/arch/arm/mach-nomadik/cpu-8815.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright STMicroelectronics, 2007.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/amba/bus.h>
+#include <linux/gpio.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <asm/mach/map.h>
+#include <asm/hardware/vic.h>
+
+#include <asm/cacheflush.h>
+#include <asm/hardware/cache-l2x0.h>
+
+/* The 8815 has 4 GPIO blocks, let's register them immediately */
+static struct nmk_gpio_platform_data cpu8815_gpio[] = {
+ {
+ .name = "GPIO-0-31",
+ .first_gpio = 0,
+ .first_irq = NOMADIK_GPIO_TO_IRQ(0),
+ .parent_irq = IRQ_GPIO0,
+ }, {
+ .name = "GPIO-32-63",
+ .first_gpio = 32,
+ .first_irq = NOMADIK_GPIO_TO_IRQ(32),
+ .parent_irq = IRQ_GPIO1,
+ }, {
+ .name = "GPIO-64-95",
+ .first_gpio = 64,
+ .first_irq = NOMADIK_GPIO_TO_IRQ(64),
+ .parent_irq = IRQ_GPIO2,
+ }, {
+ .name = "GPIO-96-127", /* 124..127 not routed to pin */
+ .first_gpio = 96,
+ .first_irq = NOMADIK_GPIO_TO_IRQ(96),
+ .parent_irq = IRQ_GPIO3,
+ }
+};
+
+#define __MEM_4K_RESOURCE(x) \
+ .res = {.start = (x), .end = (x) + SZ_4K - 1, .flags = IORESOURCE_MEM}
+
+static struct amba_device cpu8815_amba_gpio[] = {
+ {
+ .dev = {
+ .init_name = "gpio0",
+ .platform_data = cpu8815_gpio + 0,
+ },
+ __MEM_4K_RESOURCE(NOMADIK_GPIO0_BASE),
+ }, {
+ .dev = {
+ .init_name = "gpio1",
+ .platform_data = cpu8815_gpio + 1,
+ },
+ __MEM_4K_RESOURCE(NOMADIK_GPIO1_BASE),
+ }, {
+ .dev = {
+ .init_name = "gpio2",
+ .platform_data = cpu8815_gpio + 2,
+ },
+ __MEM_4K_RESOURCE(NOMADIK_GPIO2_BASE),
+ }, {
+ .dev = {
+ .init_name = "gpio3",
+ .platform_data = cpu8815_gpio + 3,
+ },
+ __MEM_4K_RESOURCE(NOMADIK_GPIO3_BASE),
+ },
+};
+
+static struct amba_device *amba_devs[] __initdata = {
+ cpu8815_amba_gpio + 0,
+ cpu8815_amba_gpio + 1,
+ cpu8815_amba_gpio + 2,
+ cpu8815_amba_gpio + 3,
+};
+
+static int __init cpu8815_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(amba_devs); i++)
+ amba_device_register(amba_devs[i], &iomem_resource);
+ return 0;
+}
+arch_initcall(cpu8815_init);
+
+/* All SoC devices live in the same area (see hardware.h) */
+static struct map_desc nomadik_io_desc[] __initdata = {
+ {
+ .virtual = NOMADIK_IO_VIRTUAL,
+ .pfn = __phys_to_pfn(NOMADIK_IO_PHYSICAL),
+ .length = NOMADIK_IO_SIZE,
+ .type = MT_DEVICE,
+ }
+ /* static ram and secured ram may be added later */
+};
+
+void __init cpu8815_map_io(void)
+{
+ iotable_init(nomadik_io_desc, ARRAY_SIZE(nomadik_io_desc));
+}
+
+void __init cpu8815_init_irq(void)
+{
+ /* This modified VIC cell has two register blocks, at 0 and 0x20 */
+ vic_init(io_p2v(NOMADIK_IC_BASE + 0x00), IRQ_VIC_START + 0, ~0, 0);
+ vic_init(io_p2v(NOMADIK_IC_BASE + 0x20), IRQ_VIC_START + 32, ~0, 0);
+}
+
+/*
+ * This function is called from the board init ("init_machine").
+ */
+ void __init cpu8815_platform_init(void)
+{
+#ifdef CONFIG_CACHE_L2X0
+ /* At full speed latency must be >=2, so 0x249 in low bits */
+ l2x0_init(io_p2v(NOMADIK_L2CC_BASE), 0x00730249, 0xfe000fff);
+#endif
+ return;
+}
diff --git a/arch/arm/mach-nomadik/gpio.c b/arch/arm/mach-nomadik/gpio.c
new file mode 100644
index 00000000000..9a09b2791e0
--- /dev/null
+++ b/arch/arm/mach-nomadik/gpio.c
@@ -0,0 +1,396 @@
+/*
+ * Generic GPIO driver for logic cells found in the Nomadik SoC
+ *
+ * Copyright (C) 2008,2009 STMicroelectronics
+ * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it>
+ * Rewritten based on work by Prafulla WADASKAR <prafulla.wadaskar@st.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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/amba/bus.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/gpio.h>
+
+/*
+ * The GPIO module in the Nomadik family of Systems-on-Chip is an
+ * AMBA device, managing 32 pins and alternate functions. The logic block
+ * is currently only used in the Nomadik.
+ *
+ * Symbols in this file are called "nmk_gpio" for "nomadik gpio"
+ */
+
+#define NMK_GPIO_PER_CHIP 32
+struct nmk_gpio_chip {
+ struct gpio_chip chip;
+ void __iomem *addr;
+ unsigned int parent_irq;
+ spinlock_t *lock;
+ /* Keep track of configured edges */
+ u32 edge_rising;
+ u32 edge_falling;
+};
+
+/* Mode functions */
+int nmk_gpio_set_mode(int gpio, int gpio_mode)
+{
+ struct nmk_gpio_chip *nmk_chip;
+ unsigned long flags;
+ u32 afunc, bfunc, bit;
+
+ nmk_chip = get_irq_chip_data(NOMADIK_GPIO_TO_IRQ(gpio));
+ if (!nmk_chip)
+ return -EINVAL;
+
+ bit = 1 << (gpio - nmk_chip->chip.base);
+
+ spin_lock_irqsave(&nmk_chip->lock, flags);
+ afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & ~bit;
+ bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & ~bit;
+ if (gpio_mode & NMK_GPIO_ALT_A)
+ afunc |= bit;
+ if (gpio_mode & NMK_GPIO_ALT_B)
+ bfunc |= bit;
+ writel(afunc, nmk_chip->addr + NMK_GPIO_AFSLA);
+ writel(bfunc, nmk_chip->addr + NMK_GPIO_AFSLB);
+ spin_unlock_irqrestore(&nmk_chip->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL(nmk_gpio_set_mode);
+
+int nmk_gpio_get_mode(int gpio)
+{
+ struct nmk_gpio_chip *nmk_chip;
+ u32 afunc, bfunc, bit;
+
+ nmk_chip = get_irq_chip_data(NOMADIK_GPIO_TO_IRQ(gpio));
+ if (!nmk_chip)
+ return -EINVAL;
+
+ bit = 1 << (gpio - nmk_chip->chip.base);
+
+ afunc = readl(nmk_chip->addr + NMK_GPIO_AFSLA) & bit;
+ bfunc = readl(nmk_chip->addr + NMK_GPIO_AFSLB) & bit;
+
+ return (afunc ? NMK_GPIO_ALT_A : 0) | (bfunc ? NMK_GPIO_ALT_B : 0);
+}
+EXPORT_SYMBOL(nmk_gpio_get_mode);
+
+
+/* IRQ functions */
+static inline int nmk_gpio_get_bitmask(int gpio)
+{
+ return 1 << (gpio % 32);
+}
+
+static void nmk_gpio_irq_ack(unsigned int irq)
+{
+ int gpio;
+ struct nmk_gpio_chip *nmk_chip;
+
+ gpio = NOMADIK_IRQ_TO_GPIO(irq);
+ nmk_chip = get_irq_chip_data(irq);
+ if (!nmk_chip)
+ return;
+ writel(nmk_gpio_get_bitmask(gpio), nmk_chip->addr + NMK_GPIO_IC);
+}
+
+static void nmk_gpio_irq_mask(unsigned int irq)
+{
+ int gpio;
+ struct nmk_gpio_chip *nmk_chip;
+ unsigned long flags;
+ u32 bitmask, reg;
+
+ gpio = NOMADIK_IRQ_TO_GPIO(irq);
+ nmk_chip = get_irq_chip_data(irq);
+ bitmask = nmk_gpio_get_bitmask(gpio);
+ if (!nmk_chip)
+ return;
+
+ /* we must individually clear the two edges */
+ spin_lock_irqsave(&nmk_chip->lock, flags);
+ if (nmk_chip->edge_rising & bitmask) {
+ reg = readl(nmk_chip->addr + NMK_GPIO_RWIMSC);
+ reg &= ~bitmask;
+ writel(reg, nmk_chip->addr + NMK_GPIO_RWIMSC);
+ }
+ if (nmk_chip->edge_falling & bitmask) {
+ reg = readl(nmk_chip->addr + NMK_GPIO_FWIMSC);
+ reg &= ~bitmask;
+ writel(reg, nmk_chip->addr + NMK_GPIO_FWIMSC);
+ }
+ spin_unlock_irqrestore(&nmk_chip->lock, flags);
+};
+
+static void nmk_gpio_irq_unmask(unsigned int irq)
+{
+ int gpio;
+ struct nmk_gpio_chip *nmk_chip;
+ unsigned long flags;
+ u32 bitmask, reg;
+
+ gpio = NOMADIK_IRQ_TO_GPIO(irq);
+ nmk_chip = get_irq_chip_data(irq);
+ bitmask = nmk_gpio_get_bitmask(gpio);
+ if (!nmk_chip)
+ return;
+
+ /* we must individually set the two edges */
+ spin_lock_irqsave(&nmk_chip->lock, flags);
+ if (nmk_chip->edge_rising & bitmask) {
+ reg = readl(nmk_chip->addr + NMK_GPIO_RWIMSC);
+ reg |= bitmask;
+ writel(reg, nmk_chip->addr + NMK_GPIO_RWIMSC);
+ }
+ if (nmk_chip->edge_falling & bitmask) {
+ reg = readl(nmk_chip->addr + NMK_GPIO_FWIMSC);
+ reg |= bitmask;
+ writel(reg, nmk_chip->addr + NMK_GPIO_FWIMSC);
+ }
+ spin_unlock_irqrestore(&nmk_chip->lock, flags);
+}
+
+static int nmk_gpio_irq_set_type(unsigned int irq, unsigned int type)
+{
+ int gpio;
+ struct nmk_gpio_chip *nmk_chip;
+ unsigned long flags;
+ u32 bitmask;
+
+ gpio = NOMADIK_IRQ_TO_GPIO(irq);
+ nmk_chip = get_irq_chip_data(irq);
+ bitmask = nmk_gpio_get_bitmask(gpio);
+ if (!nmk_chip)
+ return -EINVAL;
+
+ if (type & IRQ_TYPE_LEVEL_HIGH)
+ return -EINVAL;
+ if (type & IRQ_TYPE_LEVEL_LOW)
+ return -EINVAL;
+
+ spin_lock_irqsave(&nmk_chip->lock, flags);
+
+ nmk_chip->edge_rising &= ~bitmask;
+ if (type & IRQ_TYPE_EDGE_RISING)
+ nmk_chip->edge_rising |= bitmask;
+ writel(nmk_chip->edge_rising, nmk_chip->addr + NMK_GPIO_RIMSC);
+
+ nmk_chip->edge_falling &= ~bitmask;
+ if (type & IRQ_TYPE_EDGE_FALLING)
+ nmk_chip->edge_falling |= bitmask;
+ writel(nmk_chip->edge_falling, nmk_chip->addr + NMK_GPIO_FIMSC);
+
+ spin_unlock_irqrestore(&nmk_chip->lock, flags);
+
+ nmk_gpio_irq_unmask(irq);
+
+ return 0;
+}
+
+static struct irq_chip nmk_gpio_irq_chip = {
+ .name = "Nomadik-GPIO",
+ .ack = nmk_gpio_irq_ack,
+ .mask = nmk_gpio_irq_mask,
+ .unmask = nmk_gpio_irq_unmask,
+ .set_type = nmk_gpio_irq_set_type,
+};
+
+static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct nmk_gpio_chip *nmk_chip;
+ struct irq_chip *host_chip;
+ unsigned int gpio_irq;
+ u32 pending;
+ unsigned int first_irq;
+
+ nmk_chip = get_irq_data(irq);
+ first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base);
+ while ( (pending = readl(nmk_chip->addr + NMK_GPIO_IS)) ) {
+ gpio_irq = first_irq + __ffs(pending);
+ generic_handle_irq(gpio_irq);
+ }
+ if (0) {/* don't ack parent irq, as ack == disable */
+ host_chip = get_irq_chip(irq);
+ host_chip->ack(irq);
+ }
+}
+
+static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip)
+{
+ unsigned int first_irq;
+ int i;
+
+ first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base);
+ for (i = first_irq; i < first_irq + NMK_GPIO_PER_CHIP; i++) {
+ set_irq_chip(i, &nmk_gpio_irq_chip);
+ set_irq_handler(i, handle_edge_irq);
+ set_irq_flags(i, IRQF_VALID);
+ set_irq_chip_data(i, nmk_chip);
+ }
+ set_irq_chained_handler(nmk_chip->parent_irq, nmk_gpio_irq_handler);
+ set_irq_data(nmk_chip->parent_irq, nmk_chip);
+ return 0;
+}
+
+/* I/O Functions */
+static int nmk_gpio_make_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct nmk_gpio_chip *nmk_chip =
+ container_of(chip, struct nmk_gpio_chip, chip);
+
+ writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRC);
+ return 0;
+}
+
+static int nmk_gpio_make_output(struct gpio_chip *chip, unsigned offset,
+ int val)
+{
+ struct nmk_gpio_chip *nmk_chip =
+ container_of(chip, struct nmk_gpio_chip, chip);
+
+ writel(1 << offset, nmk_chip->addr + NMK_GPIO_DIRS);
+ return 0;
+}
+
+static int nmk_gpio_get_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct nmk_gpio_chip *nmk_chip =
+ container_of(chip, struct nmk_gpio_chip, chip);
+ u32 bit = 1 << offset;
+
+ return (readl(nmk_chip->addr + NMK_GPIO_DAT) & bit) != 0;
+}
+
+static void nmk_gpio_set_output(struct gpio_chip *chip, unsigned offset,
+ int val)
+{
+ struct nmk_gpio_chip *nmk_chip =
+ container_of(chip, struct nmk_gpio_chip, chip);
+ u32 bit = 1 << offset;
+
+ if (val)
+ writel(bit, nmk_chip->addr + NMK_GPIO_DATS);
+ else
+ writel(bit, nmk_chip->addr + NMK_GPIO_DATC);
+}
+
+/* This structure is replicated for each GPIO block allocated at probe time */
+static struct gpio_chip nmk_gpio_template = {
+ .direction_input = nmk_gpio_make_input,
+ .get = nmk_gpio_get_input,
+ .direction_output = nmk_gpio_make_output,
+ .set = nmk_gpio_set_output,
+ .ngpio = NMK_GPIO_PER_CHIP,
+ .can_sleep = 0,
+};
+
+static int __init nmk_gpio_probe(struct amba_device *dev, struct amba_id *id)
+{
+ struct nmk_gpio_platform_data *pdata;
+ struct nmk_gpio_chip *nmk_chip;
+ struct gpio_chip *chip;
+ int ret;
+
+ pdata = dev->dev.platform_data;
+ ret = amba_request_regions(dev, pdata->name);
+ if (ret)
+ return ret;
+
+ nmk_chip = kzalloc(sizeof(*nmk_chip), GFP_KERNEL);
+ if (!nmk_chip) {
+ ret = -ENOMEM;
+ goto out_amba;
+ }
+ /*
+ * The virt address in nmk_chip->addr is in the nomadik register space,
+ * so we can simply convert the resource address, without remapping
+ */
+ nmk_chip->addr = io_p2v(dev->res.start);
+ nmk_chip->chip = nmk_gpio_template;
+ nmk_chip->parent_irq = pdata->parent_irq;
+
+ chip = &nmk_chip->chip;
+ chip->base = pdata->first_gpio;
+ chip->label = pdata->name;
+ chip->dev = &dev->dev;
+ chip->owner = THIS_MODULE;
+
+ ret = gpiochip_add(&nmk_chip->chip);
+ if (ret)
+ goto out_free;
+
+ amba_set_drvdata(dev, nmk_chip);
+
+ nmk_gpio_init_irq(nmk_chip);
+
+ dev_info(&dev->dev, "Bits %i-%i at address %p\n",
+ nmk_chip->chip.base, nmk_chip->chip.base+31, nmk_chip->addr);
+ return 0;
+
+ out_free:
+ kfree(nmk_chip);
+ out_amba:
+ amba_release_regions(dev);
+ dev_err(&dev->dev, "Failure %i for GPIO %i-%i\n", ret,
+ pdata->first_gpio, pdata->first_gpio+31);
+ return ret;
+}
+
+static int nmk_gpio_remove(struct amba_device *dev)
+{
+ struct nmk_gpio_chip *nmk_chip;
+
+ nmk_chip = amba_get_drvdata(dev);
+ gpiochip_remove(&nmk_chip->chip);
+ kfree(nmk_chip);
+ amba_release_regions(dev);
+ return 0;
+}
+
+
+/* We have 0x1f080060 and 0x1f180060, accept both using the mask */
+static struct amba_id nmk_gpio_ids[] = {
+ {
+ .id = 0x1f080060,
+ .mask = 0xffefffff,
+ },
+ {0, 0},
+};
+
+static struct amba_driver nmk_gpio_driver = {
+ .drv = {
+ .owner = THIS_MODULE,
+ .name = "gpio",
+ },
+ .probe = nmk_gpio_probe,
+ .remove = nmk_gpio_remove,
+ .suspend = NULL, /* to be done */
+ .resume = NULL,
+ .id_table = nmk_gpio_ids,
+};
+
+static int __init nmk_gpio_init(void)
+{
+ return amba_driver_register(&nmk_gpio_driver);
+}
+
+arch_initcall(nmk_gpio_init);
+
+MODULE_AUTHOR("Prafulla WADASKAR and Alessandro Rubini");
+MODULE_DESCRIPTION("Nomadik GPIO Driver");
+MODULE_LICENSE("GPL");
+
+
diff --git a/arch/arm/mach-nomadik/i2c-8815nhk.c b/arch/arm/mach-nomadik/i2c-8815nhk.c
new file mode 100644
index 00000000000..abfe25a08d6
--- /dev/null
+++ b/arch/arm/mach-nomadik/i2c-8815nhk.c
@@ -0,0 +1,65 @@
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/i2c-gpio.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+
+/*
+ * There are two busses in the 8815NHK.
+ * They could, in theory, be driven by the hardware component, but we
+ * use bit-bang through GPIO by now, to keep things simple
+ */
+
+static struct i2c_gpio_platform_data nhk8815_i2c_data0 = {
+ /* keep defaults for timeouts; pins are push-pull bidirectional */
+ .scl_pin = 62,
+ .sda_pin = 63,
+};
+
+static struct i2c_gpio_platform_data nhk8815_i2c_data1 = {
+ /* keep defaults for timeouts; pins are push-pull bidirectional */
+ .scl_pin = 53,
+ .sda_pin = 54,
+};
+
+/* first bus: GPIO XX and YY */
+static struct platform_device nhk8815_i2c_dev0 = {
+ .name = "i2c-gpio",
+ .id = 0,
+ .dev = {
+ .platform_data = &nhk8815_i2c_data0,
+ },
+};
+/* second bus: GPIO XX and YY */
+static struct platform_device nhk8815_i2c_dev1 = {
+ .name = "i2c-gpio",
+ .id = 1,
+ .dev = {
+ .platform_data = &nhk8815_i2c_data1,
+ },
+};
+
+static int __init nhk8815_i2c_init(void)
+{
+ nmk_gpio_set_mode(nhk8815_i2c_data0.scl_pin, NMK_GPIO_ALT_GPIO);
+ nmk_gpio_set_mode(nhk8815_i2c_data0.sda_pin, NMK_GPIO_ALT_GPIO);
+ platform_device_register(&nhk8815_i2c_dev0);
+
+ nmk_gpio_set_mode(nhk8815_i2c_data1.scl_pin, NMK_GPIO_ALT_GPIO);
+ nmk_gpio_set_mode(nhk8815_i2c_data1.sda_pin, NMK_GPIO_ALT_GPIO);
+ platform_device_register(&nhk8815_i2c_dev1);
+
+ return 0;
+}
+
+static void __exit nhk8815_i2c_exit(void)
+{
+ platform_device_unregister(&nhk8815_i2c_dev0);
+ platform_device_unregister(&nhk8815_i2c_dev1);
+ return;
+}
+
+module_init(nhk8815_i2c_init);
+module_exit(nhk8815_i2c_exit);
diff --git a/arch/arm/mach-nomadik/include/mach/clkdev.h b/arch/arm/mach-nomadik/include/mach/clkdev.h
new file mode 100644
index 00000000000..04b37a89801
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/clkdev.h
@@ -0,0 +1,7 @@
+#ifndef __ASM_MACH_CLKDEV_H
+#define __ASM_MACH_CLKDEV_H
+
+#define __clk_get(clk) ({ 1; })
+#define __clk_put(clk) do { } while (0)
+
+#endif
diff --git a/arch/arm/mach-nomadik/include/mach/debug-macro.S b/arch/arm/mach-nomadik/include/mach/debug-macro.S
new file mode 100644
index 00000000000..e876990e156
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/debug-macro.S
@@ -0,0 +1,22 @@
+/*
+ * Debugging macro include header
+ *
+ * Copyright (C) 1994-1999 Russell King
+ * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks
+ *
+ * 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.
+ *
+*/
+
+ .macro addruart,rx
+ mrc p15, 0, \rx, c1, c0
+ tst \rx, #1 @ MMU enabled?
+ moveq \rx, #0x10000000 @ physical base address
+ movne \rx, #0xf0000000 @ virtual base
+ add \rx, \rx, #0x00100000
+ add \rx, \rx, #0x000fb000
+ .endm
+
+#include <asm/hardware/debug-pl01x.S>
diff --git a/arch/arm/mach-nomadik/include/mach/entry-macro.S b/arch/arm/mach-nomadik/include/mach/entry-macro.S
new file mode 100644
index 00000000000..49f1aa3bb42
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/entry-macro.S
@@ -0,0 +1,43 @@
+/*
+ * Low-level IRQ helper macros for Nomadik platforms
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+
+ .macro disable_fiq
+ .endm
+
+ .macro get_irqnr_preamble, base, tmp
+ ldr \base, =IO_ADDRESS(NOMADIK_IC_BASE)
+ .endm
+
+ .macro arch_ret_to_user, tmp1, tmp2
+ .endm
+
+ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
+
+ /* This stanza gets the irq mask from one of two status registers */
+ mov \irqnr, #0
+ ldr \irqstat, [\base, #VIC_REG_IRQSR0] @ get masked status
+ cmp \irqstat, #0
+ bne 1001f
+ add \irqnr, \irqnr, #32
+ ldr \irqstat, [\base, #VIC_REG_IRQSR1] @ get masked status
+
+1001: tst \irqstat, #15
+ bne 1002f
+ add \irqnr, \irqnr, #4
+ movs \irqstat, \irqstat, lsr #4
+ bne 1001b
+1002: tst \irqstat, #1
+ bne 1003f
+ add \irqnr, \irqnr, #1
+ movs \irqstat, \irqstat, lsr #1
+ bne 1002b
+1003: /* EQ will be set if no irqs pending */
+ .endm
diff --git a/arch/arm/mach-nomadik/include/mach/gpio.h b/arch/arm/mach-nomadik/include/mach/gpio.h
new file mode 100644
index 00000000000..61577c9f9a7
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/gpio.h
@@ -0,0 +1,71 @@
+/*
+ * Structures and registers for GPIO access in the Nomadik SoC
+ *
+ * Copyright (C) 2008 STMicroelectronics
+ * Author: Prafulla WADASKAR <prafulla.wadaskar@st.com>
+ * Copyright (C) 2009 Alessandro Rubini <rubini@unipv.it>
+ *
+ * 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_ARCH_GPIO_H
+#define __ASM_ARCH_GPIO_H
+
+#include <asm-generic/gpio.h>
+
+/*
+ * These currently cause a function call to happen, they may be optimized
+ * if needed by adding cpu-specific defines to identify blocks
+ * (see mach-pxa/include/mach/gpio.h as an example using GPLR etc)
+ */
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
+#define gpio_to_irq __gpio_to_irq
+
+/*
+ * "nmk_gpio" and "NMK_GPIO" stand for "Nomadik GPIO", leaving
+ * the "gpio" namespace for generic and cross-machine functions
+ */
+
+/* Register in the logic block */
+#define NMK_GPIO_DAT 0x00
+#define NMK_GPIO_DATS 0x04
+#define NMK_GPIO_DATC 0x08
+#define NMK_GPIO_PDIS 0x0c
+#define NMK_GPIO_DIR 0x10
+#define NMK_GPIO_DIRS 0x14
+#define NMK_GPIO_DIRC 0x18
+#define NMK_GPIO_SLPC 0x1c
+#define NMK_GPIO_AFSLA 0x20
+#define NMK_GPIO_AFSLB 0x24
+
+#define NMK_GPIO_RIMSC 0x40
+#define NMK_GPIO_FIMSC 0x44
+#define NMK_GPIO_IS 0x48
+#define NMK_GPIO_IC 0x4c
+#define NMK_GPIO_RWIMSC 0x50
+#define NMK_GPIO_FWIMSC 0x54
+#define NMK_GPIO_WKS 0x58
+
+/* Alternate functions: function C is set in hw by setting both A and B */
+#define NMK_GPIO_ALT_GPIO 0
+#define NMK_GPIO_ALT_A 1
+#define NMK_GPIO_ALT_B 2
+#define NMK_GPIO_ALT_C (NMK_GPIO_ALT_A | NMK_GPIO_ALT_B)
+
+extern int nmk_gpio_set_mode(int gpio, int gpio_mode);
+extern int nmk_gpio_get_mode(int gpio);
+
+/*
+ * Platform data to register a block: only the initial gpio/irq number.
+ */
+struct nmk_gpio_platform_data {
+ char *name;
+ int first_gpio;
+ int first_irq;
+ int parent_irq;
+};
+
+#endif /* __ASM_ARCH_GPIO_H */
diff --git a/arch/arm/mach-nomadik/include/mach/hardware.h b/arch/arm/mach-nomadik/include/mach/hardware.h
new file mode 100644
index 00000000000..6316dba3bfc
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/hardware.h
@@ -0,0 +1,90 @@
+/*
+ * This file contains the hardware definitions of the Nomadik.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * YOU should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_HARDWARE_H
+#define __ASM_ARCH_HARDWARE_H
+
+/* Nomadik registers live from 0x1000.0000 to 0x1023.0000 -- currently */
+#define NOMADIK_IO_VIRTUAL 0xF0000000 /* VA of IO */
+#define NOMADIK_IO_PHYSICAL 0x10000000 /* PA of IO */
+#define NOMADIK_IO_SIZE 0x00300000 /* 3MB for all regs */
+
+/* used in C code, so cast to proper type */
+#define io_p2v(x) ((void __iomem *)(x) \
+ - NOMADIK_IO_PHYSICAL + NOMADIK_IO_VIRTUAL)
+#define io_v2p(x) ((unsigned long)(x) \
+ - NOMADIK_IO_VIRTUAL + NOMADIK_IO_PHYSICAL)
+
+/* used in asm code, so no casts */
+#define IO_ADDRESS(x) ((x) - NOMADIK_IO_PHYSICAL + NOMADIK_IO_VIRTUAL)
+
+/*
+ * Base address defination for Nomadik Onchip Logic Block
+ */
+#define NOMADIK_FSMC_BASE 0x10100000 /* FSMC registers */
+#define NOMADIK_SDRAMC_BASE 0x10110000 /* SDRAM Controller */
+#define NOMADIK_CLCDC_BASE 0x10120000 /* CLCD Controller */
+#define NOMADIK_MDIF_BASE 0x10120000 /* MDIF */
+#define NOMADIK_DMA0_BASE 0x10130000 /* DMA0 Controller */
+#define NOMADIK_IC_BASE 0x10140000 /* Vectored Irq Controller */
+#define NOMADIK_DMA1_BASE 0x10150000 /* DMA1 Controller */
+#define NOMADIK_USB_BASE 0x10170000 /* USB-OTG conf reg base */
+#define NOMADIK_CRYP_BASE 0x10180000 /* Crypto processor */
+#define NOMADIK_SHA1_BASE 0x10190000 /* SHA-1 Processor */
+#define NOMADIK_XTI_BASE 0x101A0000 /* XTI */
+#define NOMADIK_RNG_BASE 0x101B0000 /* Random number generator */
+#define NOMADIK_SRC_BASE 0x101E0000 /* SRC base */
+#define NOMADIK_WDOG_BASE 0x101E1000 /* Watchdog */
+#define NOMADIK_MTU0_BASE 0x101E2000 /* Multiple Timer 0 */
+#define NOMADIK_MTU1_BASE 0x101E3000 /* Multiple Timer 1 */
+#define NOMADIK_GPIO0_BASE 0x101E4000 /* GPIO0 */
+#define NOMADIK_GPIO1_BASE 0x101E5000 /* GPIO1 */
+#define NOMADIK_GPIO2_BASE 0x101E6000 /* GPIO2 */
+#define NOMADIK_GPIO3_BASE 0x101E7000 /* GPIO3 */
+#define NOMADIK_RTC_BASE 0x101E8000 /* Real Time Clock base */
+#define NOMADIK_PMU_BASE 0x101E9000 /* Power Management Unit */
+#define NOMADIK_OWM_BASE 0x101EA000 /* One wire master */
+#define NOMADIK_SCR_BASE 0x101EF000 /* Secure Control registers */
+#define NOMADIK_MSP2_BASE 0x101F0000 /* MSP 2 interface */
+#define NOMADIK_MSP1_BASE 0x101F1000 /* MSP 1 interface */
+#define NOMADIK_UART2_BASE 0x101F2000 /* UART 2 interface */
+#define NOMADIK_SSIRx_BASE 0x101F3000 /* SSI 8-ch rx interface */
+#define NOMADIK_SSITx_BASE 0x101F4000 /* SSI 8-ch tx interface */
+#define NOMADIK_MSHC_BASE 0x101F5000 /* Memory Stick(Pro) Host */
+#define NOMADIK_SDI_BASE 0x101F6000 /* SD-card/MM-Card */
+#define NOMADIK_I2C1_BASE 0x101F7000 /* I2C1 interface */
+#define NOMADIK_I2C0_BASE 0x101F8000 /* I2C0 interface */
+#define NOMADIK_MSP0_BASE 0x101F9000 /* MSP 0 interface */
+#define NOMADIK_FIRDA_BASE 0x101FA000 /* FIrDA interface */
+#define NOMADIK_UART1_BASE 0x101FB000 /* UART 1 interface */
+#define NOMADIK_SSP_BASE 0x101FC000 /* SSP interface */
+#define NOMADIK_UART0_BASE 0x101FD000 /* UART 0 interface */
+#define NOMADIK_SGA_BASE 0x101FE000 /* SGA interface */
+#define NOMADIK_L2CC_BASE 0x10210000 /* L2 Cache controller */
+
+/* Other ranges, not for p2v/v2p */
+#define NOMADIK_BACKUP_RAM 0x80010000
+#define NOMADIK_EBROM 0x80000000 /* Embedded boot ROM */
+#define NOMADIK_HAMACV_DMEM_BASE 0xA0100000 /* HAMACV Data Memory Start */
+#define NOMADIK_HAMACV_DMEM_END 0xA01FFFFF /* HAMACV Data Memory End */
+#define NOMADIK_HAMACA_DMEM 0xA0200000 /* HAMACA Data Memory Space */
+
+#define NOMADIK_FSMC_VA IO_ADDRESS(NOMADIK_FSMC_BASE)
+#define NOMADIK_MTU0_VA IO_ADDRESS(NOMADIK_MTU0_BASE)
+#define NOMADIK_MTU1_VA IO_ADDRESS(NOMADIK_MTU1_BASE)
+
+#endif /* __ASM_ARCH_HARDWARE_H */
diff --git a/arch/arm/mach-nomadik/include/mach/io.h b/arch/arm/mach-nomadik/include/mach/io.h
new file mode 100644
index 00000000000..2e1eca1b824
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/io.h
@@ -0,0 +1,22 @@
+/*
+ * arch/arm/mach-nomadik/include/mach/io.h (copied from mach-sa1100)
+ *
+ * Copyright (C) 1997-1999 Russell King
+ *
+ * Modifications:
+ * 06-12-1997 RMK Created.
+ * 07-04-1999 RMK Major cleanup
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffffffff
+
+/*
+ * We don't actually have real ISA nor PCI buses, but there is so many
+ * drivers out there that might just work if we fake them...
+ */
+#define __io(a) __typesafe_io(a)
+#define __mem_pci(a) (a)
+
+#endif
diff --git a/arch/arm/mach-nomadik/include/mach/irqs.h b/arch/arm/mach-nomadik/include/mach/irqs.h
new file mode 100644
index 00000000000..8faabc56039
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/irqs.h
@@ -0,0 +1,82 @@
+/*
+ * mach-nomadik/include/mach/irqs.h
+ *
+ * Copyright (C) ST Microelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_IRQS_H
+#define __ASM_ARCH_IRQS_H
+
+#include <mach/hardware.h>
+
+#define IRQ_VIC_START 0 /* first VIC interrupt is 0 */
+
+/*
+ * Interrupt numbers generic for all Nomadik Chip cuts
+ */
+#define IRQ_WATCHDOG 0
+#define IRQ_SOFTINT 1
+#define IRQ_CRYPTO 2
+#define IRQ_OWM 3
+#define IRQ_MTU0 4
+#define IRQ_MTU1 5
+#define IRQ_GPIO0 6
+#define IRQ_GPIO1 7
+#define IRQ_GPIO2 8
+#define IRQ_GPIO3 9
+#define IRQ_RTC_RTT 10
+#define IRQ_SSP 11
+#define IRQ_UART0 12
+#define IRQ_DMA1 13
+#define IRQ_CLCD_MDIF 14
+#define IRQ_DMA0 15
+#define IRQ_PWRFAIL 16
+#define IRQ_UART1 17
+#define IRQ_FIRDA 18
+#define IRQ_MSP0 19
+#define IRQ_I2C0 20
+#define IRQ_I2C1 21
+#define IRQ_SDMMC 22
+#define IRQ_USBOTG 23
+#define IRQ_SVA_IT0 24
+#define IRQ_SVA_IT1 25
+#define IRQ_SAA_IT0 26
+#define IRQ_SAA_IT1 27
+#define IRQ_UART2 28
+#define IRQ_MSP2 31
+#define IRQ_L2CC 48
+#define IRQ_HPI 49
+#define IRQ_SKE 50
+#define IRQ_KP 51
+#define IRQ_MEMST 54
+#define IRQ_SGA_IT 58
+#define IRQ_USBM 60
+#define IRQ_MSP1 62
+
+#define NOMADIK_SOC_NR_IRQS 64
+
+/* After chip-specific IRQ numbers we have the GPIO ones */
+#define NOMADIK_NR_GPIO 128 /* last 4 not wired to pins */
+#define NOMADIK_GPIO_TO_IRQ(gpio) ((gpio) + NOMADIK_SOC_NR_IRQS)
+#define NOMADIK_IRQ_TO_GPIO(irq) ((irq) - NOMADIK_SOC_NR_IRQS)
+#define NR_IRQS NOMADIK_GPIO_TO_IRQ(NOMADIK_NR_GPIO)
+
+/* Following two are used by entry_macro.S, to access our dual-vic */
+#define VIC_REG_IRQSR0 0
+#define VIC_REG_IRQSR1 0x20
+
+#endif /* __ASM_ARCH_IRQS_H */
+
diff --git a/arch/arm/mach-nomadik/include/mach/memory.h b/arch/arm/mach-nomadik/include/mach/memory.h
new file mode 100644
index 00000000000..1e5689d98ec
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/memory.h
@@ -0,0 +1,28 @@
+/*
+ * mach-nomadik/include/mach/memory.h
+ *
+ * Copyright (C) 1999 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+/*
+ * Physical DRAM offset.
+ */
+#define PHYS_OFFSET UL(0x00000000)
+
+#endif
diff --git a/arch/arm/mach-nomadik/include/mach/mtu.h b/arch/arm/mach-nomadik/include/mach/mtu.h
new file mode 100644
index 00000000000..76da7f08533
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/mtu.h
@@ -0,0 +1,45 @@
+#ifndef __ASM_ARCH_MTU_H
+#define __ASM_ARCH_MTU_H
+
+/*
+ * The MTU device hosts four different counters, with 4 set of
+ * registers. These are register names.
+ */
+
+#define MTU_IMSC 0x00 /* Interrupt mask set/clear */
+#define MTU_RIS 0x04 /* Raw interrupt status */
+#define MTU_MIS 0x08 /* Masked interrupt status */
+#define MTU_ICR 0x0C /* Interrupt clear register */
+
+/* per-timer registers take 0..3 as argument */
+#define MTU_LR(x) (0x10 + 0x10 * (x) + 0x00) /* Load value */
+#define MTU_VAL(x) (0x10 + 0x10 * (x) + 0x04) /* Current value */
+#define MTU_CR(x) (0x10 + 0x10 * (x) + 0x08) /* Control reg */
+#define MTU_BGLR(x) (0x10 + 0x10 * (x) + 0x0c) /* At next overflow */
+
+/* bits for the control register */
+#define MTU_CRn_ENA 0x80
+#define MTU_CRn_PERIODIC 0x40 /* if 0 = free-running */
+#define MTU_CRn_PRESCALE_MASK 0x0c
+#define MTU_CRn_PRESCALE_1 0x00
+#define MTU_CRn_PRESCALE_16 0x04
+#define MTU_CRn_PRESCALE_256 0x08
+#define MTU_CRn_32BITS 0x02
+#define MTU_CRn_ONESHOT 0x01 /* if 0 = wraps reloading from BGLR*/
+
+/* Other registers are usual amba/primecell registers, currently not used */
+#define MTU_ITCR 0xff0
+#define MTU_ITOP 0xff4
+
+#define MTU_PERIPH_ID0 0xfe0
+#define MTU_PERIPH_ID1 0xfe4
+#define MTU_PERIPH_ID2 0xfe8
+#define MTU_PERIPH_ID3 0xfeC
+
+#define MTU_PCELL0 0xff0
+#define MTU_PCELL1 0xff4
+#define MTU_PCELL2 0xff8
+#define MTU_PCELL3 0xffC
+
+#endif /* __ASM_ARCH_MTU_H */
+
diff --git a/arch/arm/mach-nomadik/include/mach/setup.h b/arch/arm/mach-nomadik/include/mach/setup.h
new file mode 100644
index 00000000000..a4e468cf63d
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/setup.h
@@ -0,0 +1,22 @@
+
+/*
+ * These symbols are needed for board-specific files to call their
+ * own cpu-specific files
+ */
+
+#ifndef __ASM_ARCH_SETUP_H
+#define __ASM_ARCH_SETUP_H
+
+#include <asm/mach/time.h>
+#include <linux/init.h>
+
+#ifdef CONFIG_NOMADIK_8815
+
+extern void cpu8815_map_io(void);
+extern void cpu8815_platform_init(void);
+extern void cpu8815_init_irq(void);
+extern struct sys_timer nomadik_timer;
+
+#endif /* NOMADIK_8815 */
+
+#endif /* __ASM_ARCH_SETUP_H */
diff --git a/arch/arm/mach-nomadik/include/mach/system.h b/arch/arm/mach-nomadik/include/mach/system.h
new file mode 100644
index 00000000000..7119f688116
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/system.h
@@ -0,0 +1,45 @@
+/*
+ * mach-nomadik/include/mach/system.h
+ *
+ * Copyright (C) 2008 STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __ASM_ARCH_SYSTEM_H
+#define __ASM_ARCH_SYSTEM_H
+
+#include <linux/io.h>
+#include <mach/hardware.h>
+
+static inline void arch_idle(void)
+{
+ /*
+ * This should do all the clock switching
+ * and wait for interrupt tricks
+ */
+ cpu_do_idle();
+}
+
+static inline void arch_reset(char mode, const char *cmd)
+{
+ void __iomem *src_rstsr = io_p2v(NOMADIK_SRC_BASE + 0x18);
+
+ /* FIXME: use egpio when implemented */
+
+ /* Write anything to Reset status register */
+ writel(1, src_rstsr);
+}
+
+#endif
diff --git a/arch/arm/mach-nomadik/include/mach/timex.h b/arch/arm/mach-nomadik/include/mach/timex.h
new file mode 100644
index 00000000000..318b8896ce9
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/timex.h
@@ -0,0 +1,6 @@
+#ifndef __ASM_ARCH_TIMEX_H
+#define __ASM_ARCH_TIMEX_H
+
+#define CLOCK_TICK_RATE 2400000
+
+#endif
diff --git a/arch/arm/mach-nomadik/include/mach/uncompress.h b/arch/arm/mach-nomadik/include/mach/uncompress.h
new file mode 100644
index 00000000000..071003bc845
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/uncompress.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __ASM_ARCH_UNCOMPRESS_H
+#define __ASM_ARCH_UNCOMPRESS_H
+
+#include <asm/setup.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+
+/* we need the constants in amba/serial.h, but it refers to amba_device */
+struct amba_device;
+#include <linux/amba/serial.h>
+
+#define NOMADIK_UART_DR 0x101FB000
+#define NOMADIK_UART_LCRH 0x101FB02c
+#define NOMADIK_UART_CR 0x101FB030
+#define NOMADIK_UART_FR 0x101FB018
+
+static void putc(const char c)
+{
+ /* Do nothing if the UART is not enabled. */
+ if (!(readb(NOMADIK_UART_CR) & UART01x_CR_UARTEN))
+ return;
+
+ if (c == '\n')
+ putc('\r');
+
+ while (readb(NOMADIK_UART_FR) & UART01x_FR_TXFF)
+ barrier();
+ writeb(c, NOMADIK_UART_DR);
+}
+
+static void flush(void)
+{
+ if (!(readb(NOMADIK_UART_CR) & UART01x_CR_UARTEN))
+ return;
+ while (readb(NOMADIK_UART_FR) & UART01x_FR_BUSY)
+ barrier();
+}
+
+static inline void arch_decomp_setup(void)
+{
+}
+
+#define arch_decomp_wdog() /* nothing to do here */
+
+#endif /* __ASM_ARCH_UNCOMPRESS_H */
diff --git a/arch/arm/mach-nomadik/include/mach/vmalloc.h b/arch/arm/mach-nomadik/include/mach/vmalloc.h
new file mode 100644
index 00000000000..be12e31ea52
--- /dev/null
+++ b/arch/arm/mach-nomadik/include/mach/vmalloc.h
@@ -0,0 +1,2 @@
+
+#define VMALLOC_END 0xe8000000
diff --git a/arch/arm/mach-nomadik/timer.c b/arch/arm/mach-nomadik/timer.c
new file mode 100644
index 00000000000..d1738e7061d
--- /dev/null
+++ b/arch/arm/mach-nomadik/timer.c
@@ -0,0 +1,164 @@
+/*
+ * linux/arch/arm/mach-nomadik/timer.c
+ *
+ * Copyright (C) 2008 STMicroelectronics
+ * Copyright (C) 2009 Alessandro Rubini, somewhat based on at91sam926x
+ *
+ * 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/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/clockchips.h>
+#include <linux/jiffies.h>
+#include <asm/mach/time.h>
+#include <mach/mtu.h>
+
+#define TIMER_CTRL 0x80 /* No divisor */
+#define TIMER_PERIODIC 0x40
+#define TIMER_SZ32BIT 0x02
+
+/* Initial value for SRC control register: all timers use MXTAL/8 source */
+#define SRC_CR_INIT_MASK 0x00007fff
+#define SRC_CR_INIT_VAL 0x2aaa8000
+
+static u32 nmdk_count; /* accumulated count */
+static u32 nmdk_cycle; /* write-once */
+static __iomem void *mtu_base;
+
+/*
+ * clocksource: the MTU device is a decrementing counters, so we negate
+ * the value being read.
+ */
+static cycle_t nmdk_read_timer(struct clocksource *cs)
+{
+ u32 count = readl(mtu_base + MTU_VAL(0));
+ return nmdk_count + nmdk_cycle - count;
+
+}
+
+static struct clocksource nmdk_clksrc = {
+ .name = "mtu_0",
+ .rating = 120,
+ .read = nmdk_read_timer,
+ .shift = 20,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+/*
+ * Clockevent device: currently only periodic mode is supported
+ */
+static void nmdk_clkevt_mode(enum clock_event_mode mode,
+ struct clock_event_device *dev)
+{
+ unsigned long flags;
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ /* enable interrupts -- and count current value? */
+ raw_local_irq_save(flags);
+ writel(readl(mtu_base + MTU_IMSC) | 1, mtu_base + MTU_IMSC);
+ raw_local_irq_restore(flags);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ BUG(); /* Not supported, yet */
+ /* FALLTHROUGH */
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ /* disable irq */
+ raw_local_irq_save(flags);
+ writel(readl(mtu_base + MTU_IMSC) & ~1, mtu_base + MTU_IMSC);
+ raw_local_irq_restore(flags);
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ break;
+ }
+}
+
+static struct clock_event_device nmdk_clkevt = {
+ .name = "mtu_0",
+ .features = CLOCK_EVT_FEAT_PERIODIC,
+ .shift = 32,
+ .rating = 100,
+ .set_mode = nmdk_clkevt_mode,
+};
+
+/*
+ * IRQ Handler for the timer 0 of the MTU block. The irq is not shared
+ * as we are the only users of mtu0 by now.
+ */
+static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
+{
+ /* ack: "interrupt clear register" */
+ writel( 1 << 0, mtu_base + MTU_ICR);
+
+ /* we can't count lost ticks, unfortunately */
+ nmdk_count += nmdk_cycle;
+ nmdk_clkevt.event_handler(&nmdk_clkevt);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Set up timer interrupt, and return the current time in seconds.
+ */
+static struct irqaction nmdk_timer_irq = {
+ .name = "Nomadik Timer Tick",
+ .flags = IRQF_DISABLED | IRQF_TIMER,
+ .handler = nmdk_timer_interrupt,
+};
+
+static void nmdk_timer_reset(void)
+{
+ u32 cr;
+
+ writel(0, mtu_base + MTU_CR(0)); /* off */
+
+ /* configure load and background-load, and fire it up */
+ writel(nmdk_cycle, mtu_base + MTU_LR(0));
+ writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
+ cr = MTU_CRn_PERIODIC | MTU_CRn_PRESCALE_1 | MTU_CRn_32BITS;
+ writel(cr, mtu_base + MTU_CR(0));
+ writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0));
+}
+
+static void __init nmdk_timer_init(void)
+{
+ u32 src_cr;
+ unsigned long rate;
+ int bits;
+
+ rate = CLOCK_TICK_RATE; /* 2.4MHz */
+ nmdk_cycle = (rate + HZ/2) / HZ;
+
+ /* Configure timer sources in "system reset controller" ctrl reg */
+ src_cr = readl(io_p2v(NOMADIK_SRC_BASE));
+ src_cr &= SRC_CR_INIT_MASK;
+ src_cr |= SRC_CR_INIT_VAL;
+ writel(src_cr, io_p2v(NOMADIK_SRC_BASE));
+
+ /* Save global pointer to mtu, used by functions above */
+ mtu_base = io_p2v(NOMADIK_MTU0_BASE);
+
+ /* Init the timer and register clocksource */
+ nmdk_timer_reset();
+
+ nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift);
+ bits = 8*sizeof(nmdk_count);
+ nmdk_clksrc.mask = CLOCKSOURCE_MASK(bits);
+
+ clocksource_register(&nmdk_clksrc);
+
+ /* Register irq and clockevents */
+ setup_irq(IRQ_MTU0, &nmdk_timer_irq);
+ nmdk_clkevt.mult = div_sc(rate, NSEC_PER_SEC, nmdk_clkevt.shift);
+ nmdk_clkevt.cpumask = cpumask_of(0);
+ clockevents_register_device(&nmdk_clkevt);
+}
+
+struct sys_timer nomadik_timer = {
+ .init = nmdk_timer_init,
+};
diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c
index b0c7402248f..1b223076ceb 100644
--- a/arch/arm/mach-omap2/board-4430sdp.c
+++ b/arch/arm/mach-omap2/board-4430sdp.c
@@ -39,7 +39,7 @@ static struct platform_device *sdp4430_devices[] __initdata = {
};
static struct omap_uart_config sdp4430_uart_config __initdata = {
- .enabled_uarts = (1 << 0) | (1 << 1) | (1 << 2),
+ .enabled_uarts = (1 << 0) | (1 << 1) | (1 << 2) | (1 << 3),
};
static struct omap_lcd_config sdp4430_lcd_config __initdata = {
diff --git a/arch/arm/mach-omap2/mcbsp.c b/arch/arm/mach-omap2/mcbsp.c
index 0447d26d454..a846aa1ebb4 100644
--- a/arch/arm/mach-omap2/mcbsp.c
+++ b/arch/arm/mach-omap2/mcbsp.c
@@ -173,6 +173,42 @@ static struct omap_mcbsp_platform_data omap34xx_mcbsp_pdata[] = {
#define OMAP34XX_MCBSP_PDATA_SZ 0
#endif
+static struct omap_mcbsp_platform_data omap44xx_mcbsp_pdata[] = {
+ {
+ .phys_base = OMAP44XX_MCBSP1_BASE,
+ .dma_rx_sync = OMAP44XX_DMA_MCBSP1_RX,
+ .dma_tx_sync = OMAP44XX_DMA_MCBSP1_TX,
+ .rx_irq = INT_24XX_MCBSP1_IRQ_RX,
+ .tx_irq = INT_24XX_MCBSP1_IRQ_TX,
+ .ops = &omap2_mcbsp_ops,
+ },
+ {
+ .phys_base = OMAP44XX_MCBSP2_BASE,
+ .dma_rx_sync = OMAP44XX_DMA_MCBSP2_RX,
+ .dma_tx_sync = OMAP44XX_DMA_MCBSP2_TX,
+ .rx_irq = INT_24XX_MCBSP2_IRQ_RX,
+ .tx_irq = INT_24XX_MCBSP2_IRQ_TX,
+ .ops = &omap2_mcbsp_ops,
+ },
+ {
+ .phys_base = OMAP44XX_MCBSP3_BASE,
+ .dma_rx_sync = OMAP44XX_DMA_MCBSP3_RX,
+ .dma_tx_sync = OMAP44XX_DMA_MCBSP3_TX,
+ .rx_irq = INT_24XX_MCBSP3_IRQ_RX,
+ .tx_irq = INT_24XX_MCBSP3_IRQ_TX,
+ .ops = &omap2_mcbsp_ops,
+ },
+ {
+ .phys_base = OMAP44XX_MCBSP4_BASE,
+ .dma_rx_sync = OMAP44XX_DMA_MCBSP4_RX,
+ .dma_tx_sync = OMAP44XX_DMA_MCBSP4_TX,
+ .rx_irq = INT_24XX_MCBSP4_IRQ_RX,
+ .tx_irq = INT_24XX_MCBSP4_IRQ_TX,
+ .ops = &omap2_mcbsp_ops,
+ },
+};
+#define OMAP44XX_MCBSP_PDATA_SZ ARRAY_SIZE(omap44xx_mcbsp_pdata)
+
static int __init omap2_mcbsp_init(void)
{
if (cpu_is_omap2420())
@@ -181,6 +217,8 @@ static int __init omap2_mcbsp_init(void)
omap_mcbsp_count = OMAP2430_MCBSP_PDATA_SZ;
if (cpu_is_omap34xx())
omap_mcbsp_count = OMAP34XX_MCBSP_PDATA_SZ;
+ if (cpu_is_omap44xx())
+ omap_mcbsp_count = OMAP44XX_MCBSP_PDATA_SZ;
mcbsp_ptr = kzalloc(omap_mcbsp_count * sizeof(struct omap_mcbsp *),
GFP_KERNEL);
@@ -196,6 +234,9 @@ static int __init omap2_mcbsp_init(void)
if (cpu_is_omap34xx())
omap_mcbsp_register_board_cfg(omap34xx_mcbsp_pdata,
OMAP34XX_MCBSP_PDATA_SZ);
+ if (cpu_is_omap44xx())
+ omap_mcbsp_register_board_cfg(omap44xx_mcbsp_pdata,
+ OMAP44XX_MCBSP_PDATA_SZ);
return omap_mcbsp_init();
}
diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
index a7421a50410..ce22344b94e 100644
--- a/arch/arm/mach-omap2/serial.c
+++ b/arch/arm/mach-omap2/serial.c
@@ -109,6 +109,16 @@ static struct plat_serial8250_port serial_platform_data2[] = {
.regshift = 2,
.uartclk = OMAP24XX_BASE_BAUD * 16,
}, {
+#ifdef CONFIG_ARCH_OMAP4
+ .membase = IO_ADDRESS(OMAP_UART4_BASE),
+ .mapbase = OMAP_UART4_BASE,
+ .irq = 70,
+ .flags = UPF_BOOT_AUTOCONF,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = OMAP24XX_BASE_BAUD * 16,
+ }, {
+#endif
.flags = 0
}
};
diff --git a/arch/arm/mach-orion5x/Kconfig b/arch/arm/mach-orion5x/Kconfig
index 2c7035d8dcb..c3d513cad5a 100644
--- a/arch/arm/mach-orion5x/Kconfig
+++ b/arch/arm/mach-orion5x/Kconfig
@@ -89,6 +89,27 @@ config MACH_EDMINI_V2
Say 'Y' here if you want your kernel to support the
LaCie Ethernet Disk mini V2.
+config MACH_D2NET
+ bool "LaCie d2 Network"
+ select I2C_BOARDINFO
+ help
+ Say 'Y' here if you want your kernel to support the
+ LaCie d2 Network NAS.
+
+config MACH_BIGDISK
+ bool "LaCie Big Disk Network"
+ select I2C_BOARDINFO
+ help
+ Say 'Y' here if you want your kernel to support the
+ LaCie Big Disk Network NAS.
+
+config MACH_NET2BIG
+ bool "LaCie 2Big Network"
+ select I2C_BOARDINFO
+ help
+ Say 'Y' here if you want your kernel to support the
+ LaCie 2Big Network NAS.
+
config MACH_MSS2
bool "Maxtor Shared Storage II"
help
diff --git a/arch/arm/mach-orion5x/Makefile b/arch/arm/mach-orion5x/Makefile
index edc38e2c856..89772fcd65c 100644
--- a/arch/arm/mach-orion5x/Makefile
+++ b/arch/arm/mach-orion5x/Makefile
@@ -12,6 +12,9 @@ obj-$(CONFIG_MACH_WRT350N_V2) += wrt350n-v2-setup.o
obj-$(CONFIG_MACH_TS78XX) += ts78xx-setup.o
obj-$(CONFIG_MACH_MV2120) += mv2120-setup.o
obj-$(CONFIG_MACH_EDMINI_V2) += edmini_v2-setup.o
+obj-$(CONFIG_MACH_D2NET) += d2net-setup.o
+obj-$(CONFIG_MACH_BIGDISK) += d2net-setup.o
+obj-$(CONFIG_MACH_NET2BIG) += net2big-setup.o
obj-$(CONFIG_MACH_MSS2) += mss2-setup.o
obj-$(CONFIG_MACH_WNR854T) += wnr854t-setup.o
obj-$(CONFIG_MACH_RD88F5181L_GE) += rd88f5181l-ge-setup.o
diff --git a/arch/arm/mach-orion5x/addr-map.c b/arch/arm/mach-orion5x/addr-map.c
index d78731edebb..1a5d6a0e260 100644
--- a/arch/arm/mach-orion5x/addr-map.c
+++ b/arch/arm/mach-orion5x/addr-map.c
@@ -84,7 +84,8 @@ static int __init orion5x_cpu_win_can_remap(int win)
orion5x_pcie_id(&dev, &rev);
if ((dev == MV88F5281_DEV_ID && win < 4)
|| (dev == MV88F5182_DEV_ID && win < 2)
- || (dev == MV88F5181_DEV_ID && win < 2))
+ || (dev == MV88F5181_DEV_ID && win < 2)
+ || (dev == MV88F6183_DEV_ID && win < 4))
return 1;
return 0;
diff --git a/arch/arm/mach-orion5x/d2net-setup.c b/arch/arm/mach-orion5x/d2net-setup.c
new file mode 100644
index 00000000000..9d4bf763f25
--- /dev/null
+++ b/arch/arm/mach-orion5x/d2net-setup.c
@@ -0,0 +1,365 @@
+/*
+ * arch/arm/mach-orion5x/d2net-setup.c
+ *
+ * LaCie d2Network and Big Disk Network NAS setup
+ *
+ * Copyright (C) 2009 Simon Guinot <sguinot@lacie.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/pci.h>
+#include <linux/irq.h>
+#include <linux/mtd/physmap.h>
+#include <linux/mv643xx_eth.h>
+#include <linux/leds.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/ata_platform.h>
+#include <linux/gpio.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/pci.h>
+#include <mach/orion5x.h>
+#include "common.h"
+#include "mpp.h"
+
+/*****************************************************************************
+ * LaCie d2 Network Info
+ ****************************************************************************/
+
+/*
+ * 512KB NOR flash Device bus boot chip select
+ */
+
+#define D2NET_NOR_BOOT_BASE 0xfff80000
+#define D2NET_NOR_BOOT_SIZE SZ_512K
+
+/*****************************************************************************
+ * 512KB NOR Flash on Boot Device
+ ****************************************************************************/
+
+/*
+ * TODO: Check write support on flash MX29LV400CBTC-70G
+ */
+
+static struct mtd_partition d2net_partitions[] = {
+ {
+ .name = "Full512kb",
+ .size = MTDPART_SIZ_FULL,
+ .offset = 0,
+ .mask_flags = MTD_WRITEABLE,
+ },
+};
+
+static struct physmap_flash_data d2net_nor_flash_data = {
+ .width = 1,
+ .parts = d2net_partitions,
+ .nr_parts = ARRAY_SIZE(d2net_partitions),
+};
+
+static struct resource d2net_nor_flash_resource = {
+ .flags = IORESOURCE_MEM,
+ .start = D2NET_NOR_BOOT_BASE,
+ .end = D2NET_NOR_BOOT_BASE
+ + D2NET_NOR_BOOT_SIZE - 1,
+};
+
+static struct platform_device d2net_nor_flash = {
+ .name = "physmap-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &d2net_nor_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &d2net_nor_flash_resource,
+};
+
+/*****************************************************************************
+ * Ethernet
+ ****************************************************************************/
+
+static struct mv643xx_eth_platform_data d2net_eth_data = {
+ .phy_addr = MV643XX_ETH_PHY_ADDR(8),
+};
+
+/*****************************************************************************
+ * I2C devices
+ ****************************************************************************/
+
+/*
+ * i2c addr | chip | description
+ * 0x32 | Ricoh 5C372b | RTC
+ * 0x3e | GMT G762 | PWM fan controller
+ * 0x50 | HT24LC08 | eeprom (1kB)
+ *
+ * TODO: Add G762 support to the g760a driver.
+ */
+static struct i2c_board_info __initdata d2net_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("rs5c372b", 0x32),
+ }, {
+ I2C_BOARD_INFO("24c08", 0x50),
+ },
+};
+
+/*****************************************************************************
+ * SATA
+ ****************************************************************************/
+
+static struct mv_sata_platform_data d2net_sata_data = {
+ .n_ports = 2,
+};
+
+#define D2NET_GPIO_SATA0_POWER 3
+#define D2NET_GPIO_SATA1_POWER 12
+
+static void __init d2net_sata_power_init(void)
+{
+ int err;
+
+ err = gpio_request(D2NET_GPIO_SATA0_POWER, "SATA0 power");
+ if (err == 0) {
+ err = gpio_direction_output(D2NET_GPIO_SATA0_POWER, 1);
+ if (err)
+ gpio_free(D2NET_GPIO_SATA0_POWER);
+ }
+ if (err)
+ pr_err("d2net: failed to configure SATA0 power GPIO\n");
+
+ err = gpio_request(D2NET_GPIO_SATA1_POWER, "SATA1 power");
+ if (err == 0) {
+ err = gpio_direction_output(D2NET_GPIO_SATA1_POWER, 1);
+ if (err)
+ gpio_free(D2NET_GPIO_SATA1_POWER);
+ }
+ if (err)
+ pr_err("d2net: failed to configure SATA1 power GPIO\n");
+}
+
+/*****************************************************************************
+ * GPIO LED's
+ ****************************************************************************/
+
+/*
+ * The blue front LED is wired to the CPLD and can blink in relation with the
+ * SATA activity. This feature is disabled to make this LED compatible with
+ * the leds-gpio driver: MPP14 and MPP15 are configured to act like output
+ * GPIO's and have to stay in an active state. This is needed to set the blue
+ * LED in a "fix on" state regardless of the SATA activity.
+ *
+ * The following array detail the different LED registers and the combination
+ * of their possible values:
+ *
+ * led_off | blink_ctrl | SATA active | LED state
+ * | | |
+ * 1 | x | x | off
+ * 0 | 0 | 0 | off
+ * 0 | 1 | 0 | blink (rate 300ms)
+ * 0 | x | 1 | on
+ *
+ * Notes: The blue and the red front LED's can't be on at the same time.
+ * Red LED have priority.
+ */
+
+#define D2NET_GPIO_RED_LED 6
+#define D2NET_GPIO_BLUE_LED_BLINK_CTRL 16
+#define D2NET_GPIO_BLUE_LED_OFF 23
+#define D2NET_GPIO_SATA0_ACT 14
+#define D2NET_GPIO_SATA1_ACT 15
+
+static struct gpio_led d2net_leds[] = {
+ {
+ .name = "d2net:blue:power",
+ .gpio = D2NET_GPIO_BLUE_LED_OFF,
+ .active_low = 1,
+ },
+ {
+ .name = "d2net:red:fail",
+ .gpio = D2NET_GPIO_RED_LED,
+ },
+};
+
+static struct gpio_led_platform_data d2net_led_data = {
+ .num_leds = ARRAY_SIZE(d2net_leds),
+ .leds = d2net_leds,
+};
+
+static struct platform_device d2net_gpio_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &d2net_led_data,
+ },
+};
+
+static void __init d2net_gpio_leds_init(void)
+{
+ /* Configure GPIO over MPP max number. */
+ orion_gpio_set_valid(D2NET_GPIO_BLUE_LED_OFF, 1);
+
+ if (gpio_request(D2NET_GPIO_SATA0_ACT, "LED SATA0 activity") != 0)
+ return;
+ if (gpio_direction_output(D2NET_GPIO_SATA0_ACT, 1) != 0)
+ goto err_free_1;
+ if (gpio_request(D2NET_GPIO_SATA1_ACT, "LED SATA1 activity") != 0)
+ goto err_free_1;
+ if (gpio_direction_output(D2NET_GPIO_SATA1_ACT, 1) != 0)
+ goto err_free_2;
+ platform_device_register(&d2net_gpio_leds);
+ return;
+
+err_free_2:
+ gpio_free(D2NET_GPIO_SATA1_ACT);
+err_free_1:
+ gpio_free(D2NET_GPIO_SATA0_ACT);
+ return;
+}
+
+/****************************************************************************
+ * GPIO keys
+ ****************************************************************************/
+
+#define D2NET_GPIO_PUSH_BUTTON 18
+#define D2NET_GPIO_POWER_SWITCH_ON 8
+#define D2NET_GPIO_POWER_SWITCH_OFF 9
+
+#define D2NET_SWITCH_POWER_ON 0x1
+#define D2NET_SWITCH_POWER_OFF 0x2
+
+static struct gpio_keys_button d2net_buttons[] = {
+ {
+ .type = EV_SW,
+ .code = D2NET_SWITCH_POWER_OFF,
+ .gpio = D2NET_GPIO_POWER_SWITCH_OFF,
+ .desc = "Power rocker switch (auto|off)",
+ .active_low = 0,
+ },
+ {
+ .type = EV_SW,
+ .code = D2NET_SWITCH_POWER_ON,
+ .gpio = D2NET_GPIO_POWER_SWITCH_ON,
+ .desc = "Power rocker switch (on|auto)",
+ .active_low = 0,
+ },
+ {
+ .type = EV_KEY,
+ .code = KEY_POWER,
+ .gpio = D2NET_GPIO_PUSH_BUTTON,
+ .desc = "Front Push Button",
+ .active_low = 0,
+ },
+};
+
+static struct gpio_keys_platform_data d2net_button_data = {
+ .buttons = d2net_buttons,
+ .nbuttons = ARRAY_SIZE(d2net_buttons),
+};
+
+static struct platform_device d2net_gpio_buttons = {
+ .name = "gpio-keys",
+ .id = -1,
+ .dev = {
+ .platform_data = &d2net_button_data,
+ },
+};
+
+/*****************************************************************************
+ * General Setup
+ ****************************************************************************/
+
+static struct orion5x_mpp_mode d2net_mpp_modes[] __initdata = {
+ { 0, MPP_GPIO }, /* Board ID (bit 0) */
+ { 1, MPP_GPIO }, /* Board ID (bit 1) */
+ { 2, MPP_GPIO }, /* Board ID (bit 2) */
+ { 3, MPP_GPIO }, /* SATA 0 power */
+ { 4, MPP_UNUSED },
+ { 5, MPP_GPIO }, /* Fan fail detection */
+ { 6, MPP_GPIO }, /* Red front LED */
+ { 7, MPP_UNUSED },
+ { 8, MPP_GPIO }, /* Rear power switch (on|auto) */
+ { 9, MPP_GPIO }, /* Rear power switch (auto|off) */
+ { 10, MPP_UNUSED },
+ { 11, MPP_UNUSED },
+ { 12, MPP_GPIO }, /* SATA 1 power */
+ { 13, MPP_UNUSED },
+ { 14, MPP_GPIO }, /* SATA 0 active */
+ { 15, MPP_GPIO }, /* SATA 1 active */
+ { 16, MPP_GPIO }, /* Blue front LED blink control */
+ { 17, MPP_UNUSED },
+ { 18, MPP_GPIO }, /* Front button (0 = Released, 1 = Pushed ) */
+ { 19, MPP_UNUSED },
+ { -1 }
+ /* 22: USB port 1 fuse (0 = Fail, 1 = Ok) */
+ /* 23: Blue front LED off */
+ /* 24: Inhibit board power off (0 = Disabled, 1 = Enabled) */
+};
+
+static void __init d2net_init(void)
+{
+ /*
+ * Setup basic Orion functions. Need to be called early.
+ */
+ orion5x_init();
+
+ orion5x_mpp_conf(d2net_mpp_modes);
+
+ /*
+ * Configure peripherals.
+ */
+ orion5x_ehci0_init();
+ orion5x_eth_init(&d2net_eth_data);
+ orion5x_i2c_init();
+ orion5x_uart0_init();
+
+ d2net_sata_power_init();
+ orion5x_sata_init(&d2net_sata_data);
+
+ orion5x_setup_dev_boot_win(D2NET_NOR_BOOT_BASE,
+ D2NET_NOR_BOOT_SIZE);
+ platform_device_register(&d2net_nor_flash);
+
+ platform_device_register(&d2net_gpio_buttons);
+
+ d2net_gpio_leds_init();
+
+ pr_notice("d2net: Flash write are not yet supported.\n");
+
+ i2c_register_board_info(0, d2net_i2c_devices,
+ ARRAY_SIZE(d2net_i2c_devices));
+}
+
+/* Warning: LaCie use a wrong mach-type (0x20e=526) in their bootloader. */
+
+#ifdef CONFIG_MACH_D2NET
+MACHINE_START(D2NET, "LaCie d2 Network")
+ .phys_io = ORION5X_REGS_PHYS_BASE,
+ .io_pg_offst = ((ORION5X_REGS_VIRT_BASE) >> 18) & 0xFFFC,
+ .boot_params = 0x00000100,
+ .init_machine = d2net_init,
+ .map_io = orion5x_map_io,
+ .init_irq = orion5x_init_irq,
+ .timer = &orion5x_timer,
+ .fixup = tag_fixup_mem32,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_BIGDISK
+MACHINE_START(BIGDISK, "LaCie Big Disk Network")
+ .phys_io = ORION5X_REGS_PHYS_BASE,
+ .io_pg_offst = ((ORION5X_REGS_VIRT_BASE) >> 18) & 0xFFFC,
+ .boot_params = 0x00000100,
+ .init_machine = d2net_init,
+ .map_io = orion5x_map_io,
+ .init_irq = orion5x_init_irq,
+ .timer = &orion5x_timer,
+ .fixup = tag_fixup_mem32,
+MACHINE_END
+#endif
+
diff --git a/arch/arm/mach-orion5x/net2big-setup.c b/arch/arm/mach-orion5x/net2big-setup.c
new file mode 100644
index 00000000000..7bd6283476f
--- /dev/null
+++ b/arch/arm/mach-orion5x/net2big-setup.c
@@ -0,0 +1,431 @@
+/*
+ * arch/arm/mach-orion5x/net2big-setup.c
+ *
+ * LaCie 2Big Network NAS setup
+ *
+ * Copyright (C) 2009 Simon Guinot <sguinot@lacie.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/physmap.h>
+#include <linux/mv643xx_eth.h>
+#include <linux/leds.h>
+#include <linux/gpio_keys.h>
+#include <linux/input.h>
+#include <linux/i2c.h>
+#include <linux/ata_platform.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/orion5x.h>
+#include "common.h"
+#include "mpp.h"
+
+/*****************************************************************************
+ * LaCie 2Big Network Info
+ ****************************************************************************/
+
+/*
+ * 512KB NOR flash Device bus boot chip select
+ */
+
+#define NET2BIG_NOR_BOOT_BASE 0xfff80000
+#define NET2BIG_NOR_BOOT_SIZE SZ_512K
+
+/*****************************************************************************
+ * 512KB NOR Flash on Boot Device
+ ****************************************************************************/
+
+/*
+ * TODO: Check write support on flash MX29LV400CBTC-70G
+ */
+
+static struct mtd_partition net2big_partitions[] = {
+ {
+ .name = "Full512kb",
+ .size = MTDPART_SIZ_FULL,
+ .offset = 0x00000000,
+ .mask_flags = MTD_WRITEABLE,
+ },
+};
+
+static struct physmap_flash_data net2big_nor_flash_data = {
+ .width = 1,
+ .parts = net2big_partitions,
+ .nr_parts = ARRAY_SIZE(net2big_partitions),
+};
+
+static struct resource net2big_nor_flash_resource = {
+ .flags = IORESOURCE_MEM,
+ .start = NET2BIG_NOR_BOOT_BASE,
+ .end = NET2BIG_NOR_BOOT_BASE
+ + NET2BIG_NOR_BOOT_SIZE - 1,
+};
+
+static struct platform_device net2big_nor_flash = {
+ .name = "physmap-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &net2big_nor_flash_data,
+ },
+ .num_resources = 1,
+ .resource = &net2big_nor_flash_resource,
+};
+
+/*****************************************************************************
+ * Ethernet
+ ****************************************************************************/
+
+static struct mv643xx_eth_platform_data net2big_eth_data = {
+ .phy_addr = MV643XX_ETH_PHY_ADDR(8),
+};
+
+/*****************************************************************************
+ * I2C devices
+ ****************************************************************************/
+
+/*
+ * i2c addr | chip | description
+ * 0x32 | Ricoh 5C372b | RTC
+ * 0x50 | HT24LC08 | eeprom (1kB)
+ */
+static struct i2c_board_info __initdata net2big_i2c_devices[] = {
+ {
+ I2C_BOARD_INFO("rs5c372b", 0x32),
+ }, {
+ I2C_BOARD_INFO("24c08", 0x50),
+ },
+};
+
+/*****************************************************************************
+ * SATA
+ ****************************************************************************/
+
+static struct mv_sata_platform_data net2big_sata_data = {
+ .n_ports = 2,
+};
+
+#define NET2BIG_GPIO_SATA_POWER_REQ 19
+#define NET2BIG_GPIO_SATA0_POWER 23
+#define NET2BIG_GPIO_SATA1_POWER 25
+
+static void __init net2big_sata_power_init(void)
+{
+ int err;
+
+ /* Configure GPIOs over MPP max number. */
+ orion_gpio_set_valid(NET2BIG_GPIO_SATA0_POWER, 1);
+ orion_gpio_set_valid(NET2BIG_GPIO_SATA1_POWER, 1);
+
+ err = gpio_request(NET2BIG_GPIO_SATA0_POWER, "SATA0 power status");
+ if (err == 0) {
+ err = gpio_direction_input(NET2BIG_GPIO_SATA0_POWER);
+ if (err)
+ gpio_free(NET2BIG_GPIO_SATA0_POWER);
+ }
+ if (err) {
+ pr_err("net2big: failed to setup SATA0 power GPIO\n");
+ return;
+ }
+
+ err = gpio_request(NET2BIG_GPIO_SATA1_POWER, "SATA1 power status");
+ if (err == 0) {
+ err = gpio_direction_input(NET2BIG_GPIO_SATA1_POWER);
+ if (err)
+ gpio_free(NET2BIG_GPIO_SATA1_POWER);
+ }
+ if (err) {
+ pr_err("net2big: failed to setup SATA1 power GPIO\n");
+ goto err_free_1;
+ }
+
+ err = gpio_request(NET2BIG_GPIO_SATA_POWER_REQ, "SATA power request");
+ if (err == 0) {
+ err = gpio_direction_output(NET2BIG_GPIO_SATA_POWER_REQ, 0);
+ if (err)
+ gpio_free(NET2BIG_GPIO_SATA_POWER_REQ);
+ }
+ if (err) {
+ pr_err("net2big: failed to setup SATA power request GPIO\n");
+ goto err_free_2;
+ }
+
+ if (gpio_get_value(NET2BIG_GPIO_SATA0_POWER) &&
+ gpio_get_value(NET2BIG_GPIO_SATA1_POWER)) {
+ return;
+ }
+
+ /*
+ * SATA power up on both disk is done by pulling high the CPLD power
+ * request line. The 300ms delay is related to the CPLD clock and is
+ * needed to be sure that the CPLD has take into account the low line
+ * status.
+ */
+ msleep(300);
+ gpio_set_value(NET2BIG_GPIO_SATA_POWER_REQ, 1);
+ pr_info("net2big: power up SATA hard disks\n");
+
+ return;
+
+err_free_2:
+ gpio_free(NET2BIG_GPIO_SATA1_POWER);
+err_free_1:
+ gpio_free(NET2BIG_GPIO_SATA0_POWER);
+
+ return;
+}
+
+/*****************************************************************************
+ * GPIO LEDs
+ ****************************************************************************/
+
+/*
+ * The power front LEDs (blue and red) and SATA red LEDs are controlled via a
+ * single GPIO line and are compatible with the leds-gpio driver.
+ *
+ * The SATA blue LEDs have some hardware blink capabilities which are detailled
+ * in the following array:
+ *
+ * SATAx blue LED | SATAx activity | LED state
+ * | |
+ * 0 | 0 | blink (rate 300ms)
+ * 1 | 0 | off
+ * ? | 1 | on
+ *
+ * Notes: The blue and the red front LED's can't be on at the same time.
+ * Blue LED have priority.
+ */
+
+#define NET2BIG_GPIO_PWR_RED_LED 6
+#define NET2BIG_GPIO_PWR_BLUE_LED 16
+#define NET2BIG_GPIO_PWR_LED_BLINK_STOP 7
+
+#define NET2BIG_GPIO_SATA0_RED_LED 11
+#define NET2BIG_GPIO_SATA1_RED_LED 10
+
+#define NET2BIG_GPIO_SATA0_BLUE_LED 17
+#define NET2BIG_GPIO_SATA1_BLUE_LED 13
+
+static struct gpio_led net2big_leds[] = {
+ {
+ .name = "net2big:red:power",
+ .gpio = NET2BIG_GPIO_PWR_RED_LED,
+ },
+ {
+ .name = "net2big:blue:power",
+ .gpio = NET2BIG_GPIO_PWR_BLUE_LED,
+ },
+ {
+ .name = "net2big:red:sata0",
+ .gpio = NET2BIG_GPIO_SATA0_RED_LED,
+ },
+ {
+ .name = "net2big:red:sata1",
+ .gpio = NET2BIG_GPIO_SATA1_RED_LED,
+ },
+};
+
+static struct gpio_led_platform_data net2big_led_data = {
+ .num_leds = ARRAY_SIZE(net2big_leds),
+ .leds = net2big_leds,
+};
+
+static struct platform_device net2big_gpio_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &net2big_led_data,
+ },
+};
+
+static void __init net2big_gpio_leds_init(void)
+{
+ int err;
+
+ /* Stop initial CPLD slow red/blue blinking on power LED. */
+ err = gpio_request(NET2BIG_GPIO_PWR_LED_BLINK_STOP,
+ "Power LED blink stop");
+ if (err == 0) {
+ err = gpio_direction_output(NET2BIG_GPIO_PWR_LED_BLINK_STOP, 1);
+ if (err)
+ gpio_free(NET2BIG_GPIO_PWR_LED_BLINK_STOP);
+ }
+ if (err)
+ pr_err("net2big: failed to setup power LED blink GPIO\n");
+
+ /*
+ * Configure SATA0 and SATA1 blue LEDs to blink in relation with the
+ * hard disk activity.
+ */
+ err = gpio_request(NET2BIG_GPIO_SATA0_BLUE_LED,
+ "SATA0 blue LED control");
+ if (err == 0) {
+ err = gpio_direction_output(NET2BIG_GPIO_SATA0_BLUE_LED, 1);
+ if (err)
+ gpio_free(NET2BIG_GPIO_SATA0_BLUE_LED);
+ }
+ if (err)
+ pr_err("net2big: failed to setup SATA0 blue LED GPIO\n");
+
+ err = gpio_request(NET2BIG_GPIO_SATA1_BLUE_LED,
+ "SATA1 blue LED control");
+ if (err == 0) {
+ err = gpio_direction_output(NET2BIG_GPIO_SATA1_BLUE_LED, 1);
+ if (err)
+ gpio_free(NET2BIG_GPIO_SATA1_BLUE_LED);
+ }
+ if (err)
+ pr_err("net2big: failed to setup SATA1 blue LED GPIO\n");
+
+ platform_device_register(&net2big_gpio_leds);
+}
+
+/****************************************************************************
+ * GPIO keys
+ ****************************************************************************/
+
+#define NET2BIG_GPIO_PUSH_BUTTON 18
+#define NET2BIG_GPIO_POWER_SWITCH_ON 8
+#define NET2BIG_GPIO_POWER_SWITCH_OFF 9
+
+#define NET2BIG_SWITCH_POWER_ON 0x1
+#define NET2BIG_SWITCH_POWER_OFF 0x2
+
+static struct gpio_keys_button net2big_buttons[] = {
+ {
+ .type = EV_SW,
+ .code = NET2BIG_SWITCH_POWER_OFF,
+ .gpio = NET2BIG_GPIO_POWER_SWITCH_OFF,
+ .desc = "Power rocker switch (auto|off)",
+ .active_low = 0,
+ },
+ {
+ .type = EV_SW,
+ .code = NET2BIG_SWITCH_POWER_ON,
+ .gpio = NET2BIG_GPIO_POWER_SWITCH_ON,
+ .desc = "Power rocker switch (on|auto)",
+ .active_low = 0,
+ },
+ {
+ .type = EV_KEY,
+ .code = KEY_POWER,
+ .gpio = NET2BIG_GPIO_PUSH_BUTTON,
+ .desc = "Front Push Button",
+ .active_low = 0,
+ },
+};
+
+static struct gpio_keys_platform_data net2big_button_data = {
+ .buttons = net2big_buttons,
+ .nbuttons = ARRAY_SIZE(net2big_buttons),
+};
+
+static struct platform_device net2big_gpio_buttons = {
+ .name = "gpio-keys",
+ .id = -1,
+ .dev = {
+ .platform_data = &net2big_button_data,
+ },
+};
+
+/*****************************************************************************
+ * General Setup
+ ****************************************************************************/
+
+static struct orion5x_mpp_mode net2big_mpp_modes[] __initdata = {
+ { 0, MPP_GPIO }, /* Raid mode (bit 0) */
+ { 1, MPP_GPIO }, /* USB port 2 fuse (0 = Fail, 1 = Ok) */
+ { 2, MPP_GPIO }, /* Raid mode (bit 1) */
+ { 3, MPP_GPIO }, /* Board ID (bit 0) */
+ { 4, MPP_GPIO }, /* Fan activity (0 = Off, 1 = On) */
+ { 5, MPP_GPIO }, /* Fan fail detection */
+ { 6, MPP_GPIO }, /* Red front LED (0 = Off, 1 = On) */
+ { 7, MPP_GPIO }, /* Disable initial blinking on front LED */
+ { 8, MPP_GPIO }, /* Rear power switch (on|auto) */
+ { 9, MPP_GPIO }, /* Rear power switch (auto|off) */
+ { 10, MPP_GPIO }, /* SATA 1 red LED (0 = Off, 1 = On) */
+ { 11, MPP_GPIO }, /* SATA 0 red LED (0 = Off, 1 = On) */
+ { 12, MPP_GPIO }, /* Board ID (bit 1) */
+ { 13, MPP_GPIO }, /* SATA 1 blue LED blink control */
+ { 14, MPP_SATA_LED },
+ { 15, MPP_SATA_LED },
+ { 16, MPP_GPIO }, /* Blue front LED control */
+ { 17, MPP_GPIO }, /* SATA 0 blue LED blink control */
+ { 18, MPP_GPIO }, /* Front button (0 = Released, 1 = Pushed ) */
+ { 19, MPP_GPIO }, /* SATA{0,1} power On/Off request */
+ { -1 }
+ /* 22: USB port 1 fuse (0 = Fail, 1 = Ok) */
+ /* 23: SATA 0 power status */
+ /* 24: Board power off */
+ /* 25: SATA 1 power status */
+};
+
+#define NET2BIG_GPIO_POWER_OFF 24
+
+static void net2big_power_off(void)
+{
+ gpio_set_value(NET2BIG_GPIO_POWER_OFF, 1);
+}
+
+static void __init net2big_init(void)
+{
+ /*
+ * Setup basic Orion functions. Need to be called early.
+ */
+ orion5x_init();
+
+ orion5x_mpp_conf(net2big_mpp_modes);
+
+ /*
+ * Configure peripherals.
+ */
+ orion5x_ehci0_init();
+ orion5x_ehci1_init();
+ orion5x_eth_init(&net2big_eth_data);
+ orion5x_i2c_init();
+ orion5x_uart0_init();
+ orion5x_xor_init();
+
+ net2big_sata_power_init();
+ orion5x_sata_init(&net2big_sata_data);
+
+ orion5x_setup_dev_boot_win(NET2BIG_NOR_BOOT_BASE,
+ NET2BIG_NOR_BOOT_SIZE);
+ platform_device_register(&net2big_nor_flash);
+
+ platform_device_register(&net2big_gpio_buttons);
+ net2big_gpio_leds_init();
+
+ i2c_register_board_info(0, net2big_i2c_devices,
+ ARRAY_SIZE(net2big_i2c_devices));
+
+ orion_gpio_set_valid(NET2BIG_GPIO_POWER_OFF, 1);
+
+ if (gpio_request(NET2BIG_GPIO_POWER_OFF, "power-off") == 0 &&
+ gpio_direction_output(NET2BIG_GPIO_POWER_OFF, 0) == 0)
+ pm_power_off = net2big_power_off;
+ else
+ pr_err("net2big: failed to configure power-off GPIO\n");
+
+ pr_notice("net2big: Flash writing is not yet supported.\n");
+}
+
+/* Warning: LaCie use a wrong mach-type (0x20e=526) in their bootloader. */
+MACHINE_START(NET2BIG, "LaCie 2Big Network")
+ .phys_io = ORION5X_REGS_PHYS_BASE,
+ .io_pg_offst = ((ORION5X_REGS_VIRT_BASE) >> 18) & 0xFFFC,
+ .boot_params = 0x00000100,
+ .init_machine = net2big_init,
+ .map_io = orion5x_map_io,
+ .init_irq = orion5x_init_irq,
+ .timer = &orion5x_timer,
+ .fixup = tag_fixup_mem32,
+MACHINE_END
+
diff --git a/arch/arm/mach-pxa/include/mach/pxa27x_keypad.h b/arch/arm/mach-pxa/include/mach/pxa27x_keypad.h
index d5a48a96dea..7b4eadc6df3 100644
--- a/arch/arm/mach-pxa/include/mach/pxa27x_keypad.h
+++ b/arch/arm/mach-pxa/include/mach/pxa27x_keypad.h
@@ -2,9 +2,12 @@
#define __ASM_ARCH_PXA27x_KEYPAD_H
#include <linux/input.h>
+#include <linux/input/matrix_keypad.h>
#define MAX_MATRIX_KEY_ROWS (8)
#define MAX_MATRIX_KEY_COLS (8)
+#define MATRIX_ROW_SHIFT (3)
+#define MAX_DIRECT_KEY_NUM (8)
/* pxa3xx keypad platform specific parameters
*
@@ -33,7 +36,7 @@ struct pxa27x_keypad_platform_data {
/* direct keys */
int direct_key_num;
- unsigned int direct_key_map[8];
+ unsigned int direct_key_map[MAX_DIRECT_KEY_NUM];
/* rotary encoders 0 */
int enable_rotary0;
@@ -51,8 +54,6 @@ struct pxa27x_keypad_platform_data {
unsigned int debounce_interval;
};
-#define KEY(row, col, val) (((row) << 28) | ((col) << 24) | (val))
-
extern void pxa_set_keypad_info(struct pxa27x_keypad_platform_data *info);
#endif /* __ASM_ARCH_PXA27x_KEYPAD_H */
diff --git a/arch/arm/mach-pxa/sharpsl_pm.c b/arch/arm/mach-pxa/sharpsl_pm.c
index 2546c066cd6..629e05d1196 100644
--- a/arch/arm/mach-pxa/sharpsl_pm.c
+++ b/arch/arm/mach-pxa/sharpsl_pm.c
@@ -678,8 +678,8 @@ static int corgi_enter_suspend(unsigned long alarm_time, unsigned int alarm_enab
dev_dbg(sharpsl_pm.dev, "User triggered wakeup in offline charger.\n");
}
- if ((!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_LOCK)) || (sharpsl_fatal_check() < 0) )
- {
+ if ((!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_LOCK)) ||
+ (!sharpsl_pm.machinfo->read_devdata(SHARPSL_STATUS_FATAL))) {
dev_err(sharpsl_pm.dev, "Fatal condition. Suspend.\n");
corgi_goto_sleep(alarm_time, alarm_enable, state);
return 1;
diff --git a/arch/arm/mach-realview/Kconfig b/arch/arm/mach-realview/Kconfig
index d4cfa214538..dfc9b0bc6eb 100644
--- a/arch/arm/mach-realview/Kconfig
+++ b/arch/arm/mach-realview/Kconfig
@@ -75,7 +75,7 @@ config MACH_REALVIEW_PBX
config REALVIEW_HIGH_PHYS_OFFSET
bool "High physical base address for the RealView platform"
- depends on !MACH_REALVIEW_PB1176
+ depends on MMU && !MACH_REALVIEW_PB1176
default y
help
RealView boards other than PB1176 have the RAM available at
diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c
index facbd49eec6..dc3519c50ab 100644
--- a/arch/arm/mach-realview/core.c
+++ b/arch/arm/mach-realview/core.c
@@ -221,6 +221,9 @@ arch_initcall(realview_i2c_init);
#define REALVIEW_SYSMCI (__io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_MCI_OFFSET)
+/*
+ * This is only used if GPIOLIB support is disabled
+ */
static unsigned int realview_mmc_status(struct device *dev)
{
struct amba_device *adev = container_of(dev, struct amba_device, dev);
@@ -237,11 +240,15 @@ static unsigned int realview_mmc_status(struct device *dev)
struct mmc_platform_data realview_mmc0_plat_data = {
.ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
.status = realview_mmc_status,
+ .gpio_wp = 17,
+ .gpio_cd = 16,
};
struct mmc_platform_data realview_mmc1_plat_data = {
.ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
.status = realview_mmc_status,
+ .gpio_wp = 19,
+ .gpio_cd = 18,
};
/*
diff --git a/arch/arm/mach-realview/include/mach/gpio.h b/arch/arm/mach-realview/include/mach/gpio.h
new file mode 100644
index 00000000000..94ff27678a4
--- /dev/null
+++ b/arch/arm/mach-realview/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
+#define gpio_to_irq __gpio_to_irq
diff --git a/arch/arm/mach-realview/include/mach/hardware.h b/arch/arm/mach-realview/include/mach/hardware.h
index b42c14f89ac..8a638d15797 100644
--- a/arch/arm/mach-realview/include/mach/hardware.h
+++ b/arch/arm/mach-realview/include/mach/hardware.h
@@ -25,6 +25,7 @@
#include <asm/sizes.h>
/* macro to get at IO space when running virtually */
+#ifdef CONFIG_MMU
/*
* Statically mapped addresses:
*
@@ -33,6 +34,9 @@
* 1fxx xxxx -> fexx xxxx
*/
#define IO_ADDRESS(x) (((x) & 0x03ffffff) + 0xfb000000)
+#else
+#define IO_ADDRESS(x) (x)
+#endif
#define __io_address(n) __io(IO_ADDRESS(n))
#endif
diff --git a/arch/arm/mach-realview/platsmp.c b/arch/arm/mach-realview/platsmp.c
index ac0e83f1cc3..a88458b4799 100644
--- a/arch/arm/mach-realview/platsmp.c
+++ b/arch/arm/mach-realview/platsmp.c
@@ -20,6 +20,7 @@
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/localtimer.h>
+#include <asm/unified.h>
#include <mach/board-eb.h>
#include <mach/board-pb11mp.h>
@@ -137,26 +138,19 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
static void __init poke_milo(void)
{
- extern void secondary_startup(void);
-
/* nobody is to be released from the pen yet */
pen_release = -1;
/*
- * write the address of secondary startup into the system-wide
- * flags register, then clear the bottom two bits, which is what
- * BootMonitor is waiting for
+ * Write the address of secondary startup into the system-wide flags
+ * register. The BootMonitor waits for this register to become
+ * non-zero.
*/
-#if 1
#define REALVIEW_SYS_FLAGSS_OFFSET 0x30
- __raw_writel(virt_to_phys(realview_secondary_startup),
- __io_address(REALVIEW_SYS_BASE) +
- REALVIEW_SYS_FLAGSS_OFFSET);
#define REALVIEW_SYS_FLAGSC_OFFSET 0x34
- __raw_writel(3,
+ __raw_writel(BSYM(virt_to_phys(realview_secondary_startup)),
__io_address(REALVIEW_SYS_BASE) +
- REALVIEW_SYS_FLAGSC_OFFSET);
-#endif
+ REALVIEW_SYS_FLAGSS_OFFSET);
mb();
}
diff --git a/arch/arm/mach-realview/realview_eb.c b/arch/arm/mach-realview/realview_eb.c
index 8dfa44e08a9..abd13b44867 100644
--- a/arch/arm/mach-realview/realview_eb.c
+++ b/arch/arm/mach-realview/realview_eb.c
@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/sysdev.h>
#include <linux/amba/bus.h>
+#include <linux/amba/pl061.h>
#include <linux/io.h>
#include <mach/hardware.h>
@@ -113,6 +114,21 @@ static void __init realview_eb_map_io(void)
iotable_init(realview_eb11mp_io_desc, ARRAY_SIZE(realview_eb11mp_io_desc));
}
+static struct pl061_platform_data gpio0_plat_data = {
+ .gpio_base = 0,
+ .irq_base = -1,
+};
+
+static struct pl061_platform_data gpio1_plat_data = {
+ .gpio_base = 8,
+ .irq_base = -1,
+};
+
+static struct pl061_platform_data gpio2_plat_data = {
+ .gpio_base = 16,
+ .irq_base = -1,
+};
+
/*
* RealView EB AMBA devices
*/
@@ -189,9 +205,9 @@ AMBA_DEVICE(clcd, "dev:20", EB_CLCD, &clcd_plat_data);
AMBA_DEVICE(dmac, "dev:30", DMAC, NULL);
AMBA_DEVICE(sctl, "dev:e0", SCTL, NULL);
AMBA_DEVICE(wdog, "dev:e1", EB_WATCHDOG, NULL);
-AMBA_DEVICE(gpio0, "dev:e4", EB_GPIO0, NULL);
-AMBA_DEVICE(gpio1, "dev:e5", GPIO1, NULL);
-AMBA_DEVICE(gpio2, "dev:e6", GPIO2, NULL);
+AMBA_DEVICE(gpio0, "dev:e4", EB_GPIO0, &gpio0_plat_data);
+AMBA_DEVICE(gpio1, "dev:e5", GPIO1, &gpio1_plat_data);
+AMBA_DEVICE(gpio2, "dev:e6", GPIO2, &gpio2_plat_data);
AMBA_DEVICE(rtc, "dev:e8", EB_RTC, NULL);
AMBA_DEVICE(sci0, "dev:f0", SCI, NULL);
AMBA_DEVICE(uart0, "dev:f1", EB_UART0, NULL);
diff --git a/arch/arm/mach-realview/realview_pb1176.c b/arch/arm/mach-realview/realview_pb1176.c
index 25efe71a67c..17fbb0e889b 100644
--- a/arch/arm/mach-realview/realview_pb1176.c
+++ b/arch/arm/mach-realview/realview_pb1176.c
@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/sysdev.h>
#include <linux/amba/bus.h>
+#include <linux/amba/pl061.h>
#include <linux/io.h>
#include <mach/hardware.h>
@@ -107,6 +108,21 @@ static void __init realview_pb1176_map_io(void)
iotable_init(realview_pb1176_io_desc, ARRAY_SIZE(realview_pb1176_io_desc));
}
+static struct pl061_platform_data gpio0_plat_data = {
+ .gpio_base = 0,
+ .irq_base = -1,
+};
+
+static struct pl061_platform_data gpio1_plat_data = {
+ .gpio_base = 8,
+ .irq_base = -1,
+};
+
+static struct pl061_platform_data gpio2_plat_data = {
+ .gpio_base = 16,
+ .irq_base = -1,
+};
+
/*
* RealView PB1176 AMBA devices
*/
@@ -164,9 +180,9 @@ AMBA_DEVICE(uart3, "fpga:09", PB1176_UART3, NULL);
AMBA_DEVICE(smc, "dev:00", PB1176_SMC, NULL);
AMBA_DEVICE(sctl, "dev:e0", SCTL, NULL);
AMBA_DEVICE(wdog, "dev:e1", PB1176_WATCHDOG, NULL);
-AMBA_DEVICE(gpio0, "dev:e4", PB1176_GPIO0, NULL);
-AMBA_DEVICE(gpio1, "dev:e5", GPIO1, NULL);
-AMBA_DEVICE(gpio2, "dev:e6", GPIO2, NULL);
+AMBA_DEVICE(gpio0, "dev:e4", PB1176_GPIO0, &gpio0_plat_data);
+AMBA_DEVICE(gpio1, "dev:e5", GPIO1, &gpio1_plat_data);
+AMBA_DEVICE(gpio2, "dev:e6", GPIO2, &gpio2_plat_data);
AMBA_DEVICE(rtc, "dev:e8", PB1176_RTC, NULL);
AMBA_DEVICE(sci0, "dev:f0", SCI, NULL);
AMBA_DEVICE(uart0, "dev:f1", PB1176_UART0, NULL);
diff --git a/arch/arm/mach-realview/realview_pb11mp.c b/arch/arm/mach-realview/realview_pb11mp.c
index dc4b1694390..fdd042b85f4 100644
--- a/arch/arm/mach-realview/realview_pb11mp.c
+++ b/arch/arm/mach-realview/realview_pb11mp.c
@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/sysdev.h>
#include <linux/amba/bus.h>
+#include <linux/amba/pl061.h>
#include <linux/io.h>
#include <mach/hardware.h>
@@ -108,6 +109,21 @@ static void __init realview_pb11mp_map_io(void)
iotable_init(realview_pb11mp_io_desc, ARRAY_SIZE(realview_pb11mp_io_desc));
}
+static struct pl061_platform_data gpio0_plat_data = {
+ .gpio_base = 0,
+ .irq_base = -1,
+};
+
+static struct pl061_platform_data gpio1_plat_data = {
+ .gpio_base = 8,
+ .irq_base = -1,
+};
+
+static struct pl061_platform_data gpio2_plat_data = {
+ .gpio_base = 16,
+ .irq_base = -1,
+};
+
/*
* RealView PB11MPCore AMBA devices
*/
@@ -166,9 +182,9 @@ AMBA_DEVICE(uart3, "fpga:09", PB11MP_UART3, NULL);
AMBA_DEVICE(smc, "dev:00", PB11MP_SMC, NULL);
AMBA_DEVICE(sctl, "dev:e0", SCTL, NULL);
AMBA_DEVICE(wdog, "dev:e1", PB11MP_WATCHDOG, NULL);
-AMBA_DEVICE(gpio0, "dev:e4", PB11MP_GPIO0, NULL);
-AMBA_DEVICE(gpio1, "dev:e5", GPIO1, NULL);
-AMBA_DEVICE(gpio2, "dev:e6", GPIO2, NULL);
+AMBA_DEVICE(gpio0, "dev:e4", PB11MP_GPIO0, &gpio0_plat_data);
+AMBA_DEVICE(gpio1, "dev:e5", GPIO1, &gpio1_plat_data);
+AMBA_DEVICE(gpio2, "dev:e6", GPIO2, &gpio2_plat_data);
AMBA_DEVICE(rtc, "dev:e8", PB11MP_RTC, NULL);
AMBA_DEVICE(sci0, "dev:f0", SCI, NULL);
AMBA_DEVICE(uart0, "dev:f1", PB11MP_UART0, NULL);
diff --git a/arch/arm/mach-realview/realview_pba8.c b/arch/arm/mach-realview/realview_pba8.c
index d6ac1eb8657..70bba9900d9 100644
--- a/arch/arm/mach-realview/realview_pba8.c
+++ b/arch/arm/mach-realview/realview_pba8.c
@@ -23,6 +23,7 @@
#include <linux/platform_device.h>
#include <linux/sysdev.h>
#include <linux/amba/bus.h>
+#include <linux/amba/pl061.h>
#include <linux/io.h>
#include <asm/irq.h>
@@ -98,6 +99,21 @@ static void __init realview_pba8_map_io(void)
iotable_init(realview_pba8_io_desc, ARRAY_SIZE(realview_pba8_io_desc));
}
+static struct pl061_platform_data gpio0_plat_data = {
+ .gpio_base = 0,
+ .irq_base = -1,
+};
+
+static struct pl061_platform_data gpio1_plat_data = {
+ .gpio_base = 8,
+ .irq_base = -1,
+};
+
+static struct pl061_platform_data gpio2_plat_data = {
+ .gpio_base = 16,
+ .irq_base = -1,
+};
+
/*
* RealView PBA8Core AMBA devices
*/
@@ -156,9 +172,9 @@ AMBA_DEVICE(uart3, "fpga:09", PBA8_UART3, NULL);
AMBA_DEVICE(smc, "dev:00", PBA8_SMC, NULL);
AMBA_DEVICE(sctl, "dev:e0", SCTL, NULL);
AMBA_DEVICE(wdog, "dev:e1", PBA8_WATCHDOG, NULL);
-AMBA_DEVICE(gpio0, "dev:e4", PBA8_GPIO0, NULL);
-AMBA_DEVICE(gpio1, "dev:e5", GPIO1, NULL);
-AMBA_DEVICE(gpio2, "dev:e6", GPIO2, NULL);
+AMBA_DEVICE(gpio0, "dev:e4", PBA8_GPIO0, &gpio0_plat_data);
+AMBA_DEVICE(gpio1, "dev:e5", GPIO1, &gpio1_plat_data);
+AMBA_DEVICE(gpio2, "dev:e6", GPIO2, &gpio2_plat_data);
AMBA_DEVICE(rtc, "dev:e8", PBA8_RTC, NULL);
AMBA_DEVICE(sci0, "dev:f0", SCI, NULL);
AMBA_DEVICE(uart0, "dev:f1", PBA8_UART0, NULL);
diff --git a/arch/arm/mach-realview/realview_pbx.c b/arch/arm/mach-realview/realview_pbx.c
index ede2a57240a..ce6c5d25fbe 100644
--- a/arch/arm/mach-realview/realview_pbx.c
+++ b/arch/arm/mach-realview/realview_pbx.c
@@ -22,6 +22,7 @@
#include <linux/platform_device.h>
#include <linux/sysdev.h>
#include <linux/amba/bus.h>
+#include <linux/amba/pl061.h>
#include <linux/io.h>
#include <asm/irq.h>
@@ -118,6 +119,21 @@ static void __init realview_pbx_map_io(void)
iotable_init(realview_local_io_desc, ARRAY_SIZE(realview_local_io_desc));
}
+static struct pl061_platform_data gpio0_plat_data = {
+ .gpio_base = 0,
+ .irq_base = -1,
+};
+
+static struct pl061_platform_data gpio1_plat_data = {
+ .gpio_base = 8,
+ .irq_base = -1,
+};
+
+static struct pl061_platform_data gpio2_plat_data = {
+ .gpio_base = 16,
+ .irq_base = -1,
+};
+
/*
* RealView PBXCore AMBA devices
*/
@@ -176,9 +192,9 @@ AMBA_DEVICE(uart3, "fpga:09", PBX_UART3, NULL);
AMBA_DEVICE(smc, "dev:00", PBX_SMC, NULL);
AMBA_DEVICE(sctl, "dev:e0", SCTL, NULL);
AMBA_DEVICE(wdog, "dev:e1", PBX_WATCHDOG, NULL);
-AMBA_DEVICE(gpio0, "dev:e4", PBX_GPIO0, NULL);
-AMBA_DEVICE(gpio1, "dev:e5", GPIO1, NULL);
-AMBA_DEVICE(gpio2, "dev:e6", GPIO2, NULL);
+AMBA_DEVICE(gpio0, "dev:e4", PBX_GPIO0, &gpio0_plat_data);
+AMBA_DEVICE(gpio1, "dev:e5", GPIO1, &gpio1_plat_data);
+AMBA_DEVICE(gpio2, "dev:e6", GPIO2, &gpio2_plat_data);
AMBA_DEVICE(rtc, "dev:e8", PBX_RTC, NULL);
AMBA_DEVICE(sci0, "dev:f0", SCI, NULL);
AMBA_DEVICE(uart0, "dev:f1", PBX_UART0, NULL);
diff --git a/arch/arm/mach-s3c2410/Kconfig b/arch/arm/mach-s3c2410/Kconfig
index 41bb65d5b91..d8c023d4df3 100644
--- a/arch/arm/mach-s3c2410/Kconfig
+++ b/arch/arm/mach-s3c2410/Kconfig
@@ -12,6 +12,7 @@ config CPU_S3C2410
select S3C2410_GPIO
select CPU_LLSERIAL_S3C2410
select S3C2410_PM if PM
+ select S3C2410_CPUFREQ if CPU_FREQ_S3C24XX
help
Support for S3C2410 and S3C2410A family from the S3C24XX line
of Samsung Mobile CPUs.
@@ -45,6 +46,22 @@ config MACH_BAST_IDE
Internal node for machines with an BAST style IDE
interface
+# cpu frequency scaling support
+
+config S3C2410_CPUFREQ
+ bool
+ depends on CPU_FREQ_S3C24XX && CPU_S3C2410
+ select S3C2410_CPUFREQ_UTILS
+ help
+ CPU Frequency scaling support for S3C2410
+
+config S3C2410_PLLTABLE
+ bool
+ depends on S3C2410_CPUFREQ && CPU_FREQ_S3C24XX_PLL
+ default y
+ help
+ Select the PLL table for the S3C2410
+
menu "S3C2410 Machines"
config ARCH_SMDK2410
@@ -79,6 +96,7 @@ config MACH_N30
config ARCH_BAST
bool "Simtec Electronics BAST (EB2410ITX)"
select CPU_S3C2410
+ select S3C2410_IOTIMING if S3C2410_CPUFREQ
select PM_SIMTEC if PM
select SIMTEC_NOR
select MACH_BAST_IDE
diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile
index fca02f82711..2ab5ba4b266 100644
--- a/arch/arm/mach-s3c2410/Makefile
+++ b/arch/arm/mach-s3c2410/Makefile
@@ -15,6 +15,8 @@ obj-$(CONFIG_CPU_S3C2410_DMA) += dma.o
obj-$(CONFIG_CPU_S3C2410_DMA) += dma.o
obj-$(CONFIG_S3C2410_PM) += pm.o sleep.o
obj-$(CONFIG_S3C2410_GPIO) += gpio.o
+obj-$(CONFIG_S3C2410_CPUFREQ) += cpu-freq.o
+obj-$(CONFIG_S3C2410_PLLTABLE) += pll.o
# Machine support
diff --git a/arch/arm/mach-s3c2410/cpu-freq.c b/arch/arm/mach-s3c2410/cpu-freq.c
new file mode 100644
index 00000000000..9d1186877d0
--- /dev/null
+++ b/arch/arm/mach-s3c2410/cpu-freq.c
@@ -0,0 +1,159 @@
+/* linux/arch/arm/mach-s3c2410/cpu-freq.c
+ *
+ * Copyright (c) 2006,2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2410 CPU Frequency scaling
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/cpufreq.h>
+#include <linux/sysdev.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/regs-clock.h>
+
+#include <plat/cpu.h>
+#include <plat/clock.h>
+#include <plat/cpu-freq-core.h>
+
+/* Note, 2410A has an extra mode for 1:4:4 ratio, bit 2 of CLKDIV */
+
+static void s3c2410_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
+{
+ u32 clkdiv = 0;
+
+ if (cfg->divs.h_divisor == 2)
+ clkdiv |= S3C2410_CLKDIVN_HDIVN;
+
+ if (cfg->divs.p_divisor != cfg->divs.h_divisor)
+ clkdiv |= S3C2410_CLKDIVN_PDIVN;
+
+ __raw_writel(clkdiv, S3C2410_CLKDIVN);
+}
+
+static int s3c2410_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
+{
+ unsigned long hclk, fclk, pclk;
+ unsigned int hdiv, pdiv;
+ unsigned long hclk_max;
+
+ fclk = cfg->freq.fclk;
+ hclk_max = cfg->max.hclk;
+
+ cfg->freq.armclk = fclk;
+
+ s3c_freq_dbg("%s: fclk is %lu, max hclk %lu\n",
+ __func__, fclk, hclk_max);
+
+ hdiv = (fclk > cfg->max.hclk) ? 2 : 1;
+ hclk = fclk / hdiv;
+
+ if (hclk > cfg->max.hclk) {
+ s3c_freq_dbg("%s: hclk too big\n", __func__);
+ return -EINVAL;
+ }
+
+ pdiv = (hclk > cfg->max.pclk) ? 2 : 1;
+ pclk = hclk / pdiv;
+
+ if (pclk > cfg->max.pclk) {
+ s3c_freq_dbg("%s: pclk too big\n", __func__);
+ return -EINVAL;
+ }
+
+ pdiv *= hdiv;
+
+ /* record the result */
+ cfg->divs.p_divisor = pdiv;
+ cfg->divs.h_divisor = hdiv;
+
+ return 0 ;
+}
+
+static struct s3c_cpufreq_info s3c2410_cpufreq_info = {
+ .max = {
+ .fclk = 200000000,
+ .hclk = 100000000,
+ .pclk = 50000000,
+ },
+
+ /* transition latency is about 5ms worst-case, so
+ * set 10ms to be sure */
+ .latency = 10000000,
+
+ .locktime_m = 150,
+ .locktime_u = 150,
+ .locktime_bits = 12,
+
+ .need_pll = 1,
+
+ .name = "s3c2410",
+ .calc_iotiming = s3c2410_iotiming_calc,
+ .set_iotiming = s3c2410_iotiming_set,
+ .get_iotiming = s3c2410_iotiming_get,
+ .resume_clocks = s3c2410_setup_clocks,
+
+ .set_fvco = s3c2410_set_fvco,
+ .set_refresh = s3c2410_cpufreq_setrefresh,
+ .set_divs = s3c2410_cpufreq_setdivs,
+ .calc_divs = s3c2410_cpufreq_calcdivs,
+
+ .debug_io_show = s3c_cpufreq_debugfs_call(s3c2410_iotiming_debugfs),
+};
+
+static int s3c2410_cpufreq_add(struct sys_device *sysdev)
+{
+ return s3c_cpufreq_register(&s3c2410_cpufreq_info);
+}
+
+static struct sysdev_driver s3c2410_cpufreq_driver = {
+ .add = s3c2410_cpufreq_add,
+};
+
+static int __init s3c2410_cpufreq_init(void)
+{
+ return sysdev_driver_register(&s3c2410_sysclass,
+ &s3c2410_cpufreq_driver);
+}
+
+arch_initcall(s3c2410_cpufreq_init);
+
+static int s3c2410a_cpufreq_add(struct sys_device *sysdev)
+{
+ /* alter the maximum freq settings for S3C2410A. If a board knows
+ * it only has a maximum of 200, then it should register its own
+ * limits. */
+
+ s3c2410_cpufreq_info.max.fclk = 266000000;
+ s3c2410_cpufreq_info.max.hclk = 133000000;
+ s3c2410_cpufreq_info.max.pclk = 66500000;
+ s3c2410_cpufreq_info.name = "s3c2410a";
+
+ return s3c2410_cpufreq_add(sysdev);
+}
+
+static struct sysdev_driver s3c2410a_cpufreq_driver = {
+ .add = s3c2410a_cpufreq_add,
+};
+
+static int __init s3c2410a_cpufreq_init(void)
+{
+ return sysdev_driver_register(&s3c2410a_sysclass,
+ &s3c2410a_cpufreq_driver);
+}
+
+arch_initcall(s3c2410a_cpufreq_init);
diff --git a/arch/arm/mach-s3c2410/dma.c b/arch/arm/mach-s3c2410/dma.c
index dbf96e60d99..63b753f56c6 100644
--- a/arch/arm/mach-s3c2410/dma.c
+++ b/arch/arm/mach-s3c2410/dma.c
@@ -164,6 +164,17 @@ static int __init s3c2410_dma_drvinit(void)
}
arch_initcall(s3c2410_dma_drvinit);
+
+static struct sysdev_driver s3c2410a_dma_driver = {
+ .add = s3c2410_dma_add,
+};
+
+static int __init s3c2410a_dma_drvinit(void)
+{
+ return sysdev_driver_register(&s3c2410a_sysclass, &s3c2410a_dma_driver);
+}
+
+arch_initcall(s3c2410a_dma_drvinit);
#endif
#if defined(CONFIG_CPU_S3C2442)
diff --git a/arch/arm/mach-s3c2410/include/mach/irqs.h b/arch/arm/mach-s3c2410/include/mach/irqs.h
index 2a2384ffa7b..6c12c6312ad 100644
--- a/arch/arm/mach-s3c2410/include/mach/irqs.h
+++ b/arch/arm/mach-s3c2410/include/mach/irqs.h
@@ -164,6 +164,12 @@
#define IRQ_S3CUART_TX3 IRQ_S3C2443_TX3
#define IRQ_S3CUART_ERR3 IRQ_S3C2443_ERR3
+#ifdef CONFIG_CPU_S3C2440
+#define IRQ_S3C244x_AC97 IRQ_S3C2440_AC97
+#else
+#define IRQ_S3C244x_AC97 IRQ_S3C2443_AC97
+#endif
+
/* Our FIQs are routable from IRQ_EINT0 to IRQ_ADCPARENT */
#define FIQ_START IRQ_EINT0
diff --git a/arch/arm/mach-s3c2410/include/mach/map.h b/arch/arm/mach-s3c2410/include/mach/map.h
index e99b212cb1c..b049e61460b 100644
--- a/arch/arm/mach-s3c2410/include/mach/map.h
+++ b/arch/arm/mach-s3c2410/include/mach/map.h
@@ -67,6 +67,13 @@
#define S3C2443_PA_HSMMC (0x4A800000)
#define S3C2443_SZ_HSMMC (256)
+/* S3C2412 memory and IO controls */
+#define S3C2412_PA_SSMC (0x4F000000)
+#define S3C2412_VA_SSMC S3C_ADDR_CPU(0x00000000)
+
+#define S3C2412_PA_EBI (0x48800000)
+#define S3C2412_VA_EBI S3C_ADDR_CPU(0x00010000)
+
/* physical addresses of all the chip-select areas */
#define S3C2410_CS0 (0x00000000)
@@ -103,5 +110,6 @@
#define S3C_PA_UART S3C24XX_PA_UART
#define S3C_PA_USBHOST S3C2410_PA_USBHOST
#define S3C_PA_HSMMC0 S3C2443_PA_HSMMC
+#define S3C_PA_NAND S3C24XX_PA_NAND
#endif /* __ASM_ARCH_MAP_H */
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-gpio.h b/arch/arm/mach-s3c2410/include/mach/regs-gpio.h
index b278d0c45cc..f6e8eec879c 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-gpio.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-gpio.h
@@ -328,13 +328,15 @@
#define S3C2410_GPD8_VD16 (0x02 << 16)
#define S3C2400_GPD8_TOUT3 (0x02 << 16)
+#define S3C2440_GPD8_SPIMISO1 (0x03 << 16)
#define S3C2410_GPD9_VD17 (0x02 << 18)
#define S3C2400_GPD9_TCLK0 (0x02 << 18)
-#define S3C2410_GPD9_MASK (0x03 << 18)
+#define S3C2440_GPD9_SPIMOSI1 (0x03 << 18)
#define S3C2410_GPD10_VD18 (0x02 << 20)
#define S3C2400_GPD10_nWAIT (0x02 << 20)
+#define S3C2440_GPD10_SPICLK1 (0x03 << 20)
#define S3C2410_GPD11_VD19 (0x02 << 22)
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-mem.h b/arch/arm/mach-s3c2410/include/mach/regs-mem.h
index 57759804e2f..7f7c5294796 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-mem.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-mem.h
@@ -73,6 +73,16 @@
#define S3C2410_BWSCON_WS7 (1<<30)
#define S3C2410_BWSCON_ST7 (1<<31)
+/* accesor functions for getting BANK(n) configuration. (n != 0) */
+
+#define S3C2410_BWSCON_GET(_bwscon, _bank) (((_bwscon) >> ((_bank) * 4)) & 0xf)
+
+#define S3C2410_BWSCON_DW8 (0)
+#define S3C2410_BWSCON_DW16 (1)
+#define S3C2410_BWSCON_DW32 (2)
+#define S3C2410_BWSCON_WS (1 << 2)
+#define S3C2410_BWSCON_ST (1 << 3)
+
/* memory set (rom, ram) */
#define S3C2410_BANKCON0 S3C2410_MEMREG(0x0004)
#define S3C2410_BANKCON1 S3C2410_MEMREG(0x0008)
diff --git a/arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h b/arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h
index a4bf2712317..fb635251509 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-s3c2412-mem.h
@@ -14,9 +14,11 @@
#ifndef __ASM_ARM_REGS_S3C2412_MEM
#define __ASM_ARM_REGS_S3C2412_MEM
-#ifndef S3C2412_MEMREG
#define S3C2412_MEMREG(x) (S3C24XX_VA_MEMCTRL + (x))
-#endif
+#define S3C2412_EBIREG(x) (S3C2412_VA_EBI + (x))
+
+#define S3C2412_SSMCREG(x) (S3C2412_VA_SSMC + (x))
+#define S3C2412_SSMC(x, o) (S3C2412_SSMCREG((x * 0x20) + (o)))
#define S3C2412_BANKCFG S3C2412_MEMREG(0x00)
#define S3C2412_BANKCON1 S3C2412_MEMREG(0x04)
@@ -26,4 +28,21 @@
#define S3C2412_REFRESH S3C2412_MEMREG(0x10)
#define S3C2412_TIMEOUT S3C2412_MEMREG(0x14)
+/* EBI control registers */
+
+#define S3C2412_EBI_PR S3C2412_EBIREG(0x00)
+#define S3C2412_EBI_BANKCFG S3C2412_EBIREG(0x04)
+
+/* SSMC control registers */
+
+#define S3C2412_SSMC_BANK(x) S3C2412_SSMC(x, 0x00)
+#define S3C2412_SMIDCYR(x) S3C2412_SSMC(x, 0x00)
+#define S3C2412_SMBWSTRD(x) S3C2412_SSMC(x, 0x04)
+#define S3C2412_SMBWSTWRR(x) S3C2412_SSMC(x, 0x08)
+#define S3C2412_SMBWSTOENR(x) S3C2412_SSMC(x, 0x0C)
+#define S3C2412_SMBWSTWENR(x) S3C2412_SSMC(x, 0x10)
+#define S3C2412_SMBCR(x) S3C2412_SSMC(x, 0x14)
+#define S3C2412_SMBSR(x) S3C2412_SSMC(x, 0x18)
+#define S3C2412_SMBWSTBRDR(x) S3C2412_SSMC(x, 0x1C)
+
#endif /* __ASM_ARM_REGS_S3C2412_MEM */
diff --git a/arch/arm/mach-s3c2410/include/mach/spi.h b/arch/arm/mach-s3c2410/include/mach/spi.h
index 1d300fb112b..193b39d654e 100644
--- a/arch/arm/mach-s3c2410/include/mach/spi.h
+++ b/arch/arm/mach-s3c2410/include/mach/spi.h
@@ -30,4 +30,7 @@ extern void s3c24xx_spi_gpiocfg_bus0_gpe11_12_13(struct s3c2410_spi_info *spi,
extern void s3c24xx_spi_gpiocfg_bus1_gpg5_6_7(struct s3c2410_spi_info *spi,
int enable);
+extern void s3c24xx_spi_gpiocfg_bus1_gpd8_9_10(struct s3c2410_spi_info *spi,
+ int enable);
+
#endif /* __ASM_ARCH_SPI_H */
diff --git a/arch/arm/mach-s3c2410/irq.c b/arch/arm/mach-s3c2410/irq.c
index 92150399563..5e2f3533205 100644
--- a/arch/arm/mach-s3c2410/irq.c
+++ b/arch/arm/mach-s3c2410/irq.c
@@ -39,9 +39,22 @@ static struct sysdev_driver s3c2410_irq_driver = {
.resume = s3c24xx_irq_resume,
};
-static int s3c2410_irq_init(void)
+static int __init s3c2410_irq_init(void)
{
return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_irq_driver);
}
arch_initcall(s3c2410_irq_init);
+
+static struct sysdev_driver s3c2410a_irq_driver = {
+ .add = s3c2410_irq_add,
+ .suspend = s3c24xx_irq_suspend,
+ .resume = s3c24xx_irq_resume,
+};
+
+static int __init s3c2410a_irq_init(void)
+{
+ return sysdev_driver_register(&s3c2410a_sysclass, &s3c2410a_irq_driver);
+}
+
+arch_initcall(s3c2410a_irq_init);
diff --git a/arch/arm/mach-s3c2410/mach-bast.c b/arch/arm/mach-s3c2410/mach-bast.c
index ce3baba2cd7..647c9adb018 100644
--- a/arch/arm/mach-s3c2410/mach-bast.c
+++ b/arch/arm/mach-s3c2410/mach-bast.c
@@ -45,6 +45,7 @@
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
+#include <plat/hwmon.h>
#include <plat/nand.h>
#include <plat/iic.h>
#include <mach/fb.h>
@@ -59,6 +60,7 @@
#include <plat/clock.h>
#include <plat/devs.h>
#include <plat/cpu.h>
+#include <plat/cpu-freq.h>
#include "usb-simtec.h"
#include "nor-simtec.h"
@@ -547,7 +549,35 @@ static struct i2c_board_info bast_i2c_devs[] __initdata = {
},
};
+static struct s3c_hwmon_pdata bast_hwmon_info = {
+ /* LCD contrast (0-6.6V) */
+ .in[0] = &(struct s3c_hwmon_chcfg) {
+ .name = "lcd-contrast",
+ .mult = 3300,
+ .div = 512,
+ },
+ /* LED current feedback */
+ .in[1] = &(struct s3c_hwmon_chcfg) {
+ .name = "led-feedback",
+ .mult = 3300,
+ .div = 1024,
+ },
+ /* LCD feedback (0-6.6V) */
+ .in[2] = &(struct s3c_hwmon_chcfg) {
+ .name = "lcd-feedback",
+ .mult = 3300,
+ .div = 512,
+ },
+ /* Vcore (1.8-2.0V), Vref 3.3V */
+ .in[3] = &(struct s3c_hwmon_chcfg) {
+ .name = "vcore",
+ .mult = 3300,
+ .div = 1024,
+ },
+};
+
/* Standard BAST devices */
+// cat /sys/devices/platform/s3c24xx-adc/s3c-hwmon/in_0
static struct platform_device *bast_devices[] __initdata = {
&s3c_device_usb,
@@ -556,6 +586,8 @@ static struct platform_device *bast_devices[] __initdata = {
&s3c_device_i2c0,
&s3c_device_rtc,
&s3c_device_nand,
+ &s3c_device_adc,
+ &s3c_device_hwmon,
&bast_device_dm9k,
&bast_device_asix,
&bast_device_axpp,
@@ -570,6 +602,12 @@ static struct clk *bast_clocks[] __initdata = {
&s3c24xx_uclk,
};
+static struct s3c_cpufreq_board __initdata bast_cpufreq = {
+ .refresh = 7800, /* 7.8usec */
+ .auto_io = 1,
+ .need_io = 1,
+};
+
static void __init bast_map_io(void)
{
/* initialise the clocks */
@@ -588,6 +626,7 @@ static void __init bast_map_io(void)
s3c24xx_register_clocks(bast_clocks, ARRAY_SIZE(bast_clocks));
s3c_device_nand.dev.platform_data = &bast_nand_info;
+ s3c_device_hwmon.dev.platform_data = &bast_hwmon_info;
s3c24xx_init_io(bast_iodesc, ARRAY_SIZE(bast_iodesc));
s3c24xx_init_clocks(0);
@@ -608,6 +647,8 @@ static void __init bast_init(void)
usb_simtec_init();
nor_simtec_init();
+
+ s3c_cpufreq_setboard(&bast_cpufreq);
}
MACHINE_START(BAST, "Simtec-BAST")
diff --git a/arch/arm/mach-s3c2410/pll.c b/arch/arm/mach-s3c2410/pll.c
new file mode 100644
index 00000000000..f178c2fd9d8
--- /dev/null
+++ b/arch/arm/mach-s3c2410/pll.c
@@ -0,0 +1,95 @@
+/* arch/arm/mach-s3c2410/pll.c
+ *
+ * Copyright (c) 2006,2007 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ * Vincent Sanders <vince@arm.linux.org.uk>
+ *
+ * S3C2410 CPU PLL tables
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sysdev.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <plat/cpu.h>
+#include <plat/cpu-freq-core.h>
+
+static struct cpufreq_frequency_table pll_vals_12MHz[] = {
+ { .frequency = 34000000, .index = PLLVAL(82, 2, 3), },
+ { .frequency = 45000000, .index = PLLVAL(82, 1, 3), },
+ { .frequency = 51000000, .index = PLLVAL(161, 3, 3), },
+ { .frequency = 48000000, .index = PLLVAL(120, 2, 3), },
+ { .frequency = 56000000, .index = PLLVAL(142, 2, 3), },
+ { .frequency = 68000000, .index = PLLVAL(82, 2, 2), },
+ { .frequency = 79000000, .index = PLLVAL(71, 1, 2), },
+ { .frequency = 85000000, .index = PLLVAL(105, 2, 2), },
+ { .frequency = 90000000, .index = PLLVAL(112, 2, 2), },
+ { .frequency = 101000000, .index = PLLVAL(127, 2, 2), },
+ { .frequency = 113000000, .index = PLLVAL(105, 1, 2), },
+ { .frequency = 118000000, .index = PLLVAL(150, 2, 2), },
+ { .frequency = 124000000, .index = PLLVAL(116, 1, 2), },
+ { .frequency = 135000000, .index = PLLVAL(82, 2, 1), },
+ { .frequency = 147000000, .index = PLLVAL(90, 2, 1), },
+ { .frequency = 152000000, .index = PLLVAL(68, 1, 1), },
+ { .frequency = 158000000, .index = PLLVAL(71, 1, 1), },
+ { .frequency = 170000000, .index = PLLVAL(77, 1, 1), },
+ { .frequency = 180000000, .index = PLLVAL(82, 1, 1), },
+ { .frequency = 186000000, .index = PLLVAL(85, 1, 1), },
+ { .frequency = 192000000, .index = PLLVAL(88, 1, 1), },
+ { .frequency = 203000000, .index = PLLVAL(161, 3, 1), },
+
+ /* 2410A extras */
+
+ { .frequency = 210000000, .index = PLLVAL(132, 2, 1), },
+ { .frequency = 226000000, .index = PLLVAL(105, 1, 1), },
+ { .frequency = 266000000, .index = PLLVAL(125, 1, 1), },
+ { .frequency = 268000000, .index = PLLVAL(126, 1, 1), },
+ { .frequency = 270000000, .index = PLLVAL(127, 1, 1), },
+};
+
+static int s3c2410_plls_add(struct sys_device *dev)
+{
+ return s3c_plltab_register(pll_vals_12MHz, ARRAY_SIZE(pll_vals_12MHz));
+}
+
+static struct sysdev_driver s3c2410_plls_drv = {
+ .add = s3c2410_plls_add,
+};
+
+static int __init s3c2410_pll_init(void)
+{
+ return sysdev_driver_register(&s3c2410_sysclass, &s3c2410_plls_drv);
+
+}
+
+arch_initcall(s3c2410_pll_init);
+
+static struct sysdev_driver s3c2410a_plls_drv = {
+ .add = s3c2410_plls_add,
+};
+
+static int __init s3c2410a_pll_init(void)
+{
+ return sysdev_driver_register(&s3c2410a_sysclass, &s3c2410a_plls_drv);
+}
+
+arch_initcall(s3c2410a_pll_init);
diff --git a/arch/arm/mach-s3c2410/pm.c b/arch/arm/mach-s3c2410/pm.c
index 143e08a599d..966119c8efe 100644
--- a/arch/arm/mach-s3c2410/pm.c
+++ b/arch/arm/mach-s3c2410/pm.c
@@ -119,6 +119,18 @@ static int __init s3c2410_pm_drvinit(void)
}
arch_initcall(s3c2410_pm_drvinit);
+
+static struct sysdev_driver s3c2410a_pm_driver = {
+ .add = s3c2410_pm_add,
+ .resume = s3c2410_pm_resume,
+};
+
+static int __init s3c2410a_pm_drvinit(void)
+{
+ return sysdev_driver_register(&s3c2410a_sysclass, &s3c2410a_pm_driver);
+}
+
+arch_initcall(s3c2410a_pm_drvinit);
#endif
#if defined(CONFIG_CPU_S3C2440)
diff --git a/arch/arm/mach-s3c2410/s3c2410.c b/arch/arm/mach-s3c2410/s3c2410.c
index feb141b1f91..91ba42f688a 100644
--- a/arch/arm/mach-s3c2410/s3c2410.c
+++ b/arch/arm/mach-s3c2410/s3c2410.c
@@ -105,17 +105,33 @@ void __init_or_cpufreq s3c2410_setup_clocks(void)
s3c24xx_setup_clocks(fclk, hclk, pclk);
}
+/* fake ARMCLK for use with cpufreq, etc. */
+
+static struct clk s3c2410_armclk = {
+ .name = "armclk",
+ .parent = &clk_f,
+ .id = -1,
+};
+
void __init s3c2410_init_clocks(int xtal)
{
s3c24xx_register_baseclocks(xtal);
s3c2410_setup_clocks();
s3c2410_baseclk_add();
+ s3c24xx_register_clock(&s3c2410_armclk);
}
struct sysdev_class s3c2410_sysclass = {
.name = "s3c2410-core",
};
+/* Note, we would have liked to name this s3c2410-core, but we cannot
+ * register two sysdev_class with the same name.
+ */
+struct sysdev_class s3c2410a_sysclass = {
+ .name = "s3c2410a-core",
+};
+
static struct sys_device s3c2410_sysdev = {
.cls = &s3c2410_sysclass,
};
@@ -133,9 +149,22 @@ static int __init s3c2410_core_init(void)
core_initcall(s3c2410_core_init);
+static int __init s3c2410a_core_init(void)
+{
+ return sysdev_class_register(&s3c2410a_sysclass);
+}
+
+core_initcall(s3c2410a_core_init);
+
int __init s3c2410_init(void)
{
printk("S3C2410: Initialising architecture\n");
return sysdev_register(&s3c2410_sysdev);
}
+
+int __init s3c2410a_init(void)
+{
+ s3c2410_sysdev.cls = &s3c2410a_sysclass;
+ return s3c2410_init();
+}
diff --git a/arch/arm/mach-s3c2412/Kconfig b/arch/arm/mach-s3c2412/Kconfig
index 63586ffd0ae..35c1bde89cf 100644
--- a/arch/arm/mach-s3c2412/Kconfig
+++ b/arch/arm/mach-s3c2412/Kconfig
@@ -32,6 +32,15 @@ config S3C2412_PM
help
Internal config node to apply S3C2412 power management
+# Note, the S3C2412 IOtiming support is in plat-s3c24xx
+
+config S3C2412_CPUFREQ
+ bool
+ depends on CPU_FREQ_S3C24XX && CPU_S3C2412
+ select S3C2412_IOTIMING
+ default y
+ help
+ CPU Frequency scaling support for S3C2412 and S3C2413 SoC CPUs.
menu "S3C2412 Machines"
diff --git a/arch/arm/mach-s3c2412/Makefile b/arch/arm/mach-s3c2412/Makefile
index 20918d5dc6a..530ec46cbae 100644
--- a/arch/arm/mach-s3c2412/Makefile
+++ b/arch/arm/mach-s3c2412/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_CPU_S3C2412) += clock.o
obj-$(CONFIG_CPU_S3C2412) += gpio.o
obj-$(CONFIG_S3C2412_DMA) += dma.o
obj-$(CONFIG_S3C2412_PM) += pm.o sleep.o
+obj-$(CONFIG_S3C2412_CPUFREQ) += cpu-freq.o
# Machine support
diff --git a/arch/arm/mach-s3c2412/cpu-freq.c b/arch/arm/mach-s3c2412/cpu-freq.c
new file mode 100644
index 00000000000..eb3ea172133
--- /dev/null
+++ b/arch/arm/mach-s3c2412/cpu-freq.c
@@ -0,0 +1,257 @@
+/* linux/arch/arm/mach-s3c2412/cpu-freq.c
+ *
+ * Copyright 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2412 CPU Frequency scalling
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/cpufreq.h>
+#include <linux/sysdev.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/regs-clock.h>
+#include <mach/regs-s3c2412-mem.h>
+
+#include <plat/cpu.h>
+#include <plat/clock.h>
+#include <plat/cpu-freq-core.h>
+
+/* our clock resources. */
+static struct clk *xtal;
+static struct clk *fclk;
+static struct clk *hclk;
+static struct clk *armclk;
+
+/* HDIV: 1, 2, 3, 4, 6, 8 */
+
+static int s3c2412_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
+{
+ unsigned int hdiv, pdiv, armdiv, dvs;
+ unsigned long hclk, fclk, armclk, armdiv_clk;
+ unsigned long hclk_max;
+
+ fclk = cfg->freq.fclk;
+ armclk = cfg->freq.armclk;
+ hclk_max = cfg->max.hclk;
+
+ /* We can't run hclk above armclk as at the best we have to
+ * have armclk and hclk in dvs mode. */
+
+ if (hclk_max > armclk)
+ hclk_max = armclk;
+
+ s3c_freq_dbg("%s: fclk=%lu, armclk=%lu, hclk_max=%lu\n",
+ __func__, fclk, armclk, hclk_max);
+ s3c_freq_dbg("%s: want f=%lu, arm=%lu, h=%lu, p=%lu\n",
+ __func__, cfg->freq.fclk, cfg->freq.armclk,
+ cfg->freq.hclk, cfg->freq.pclk);
+
+ armdiv = fclk / armclk;
+
+ if (armdiv < 1)
+ armdiv = 1;
+ if (armdiv > 2)
+ armdiv = 2;
+
+ cfg->divs.arm_divisor = armdiv;
+ armdiv_clk = fclk / armdiv;
+
+ hdiv = armdiv_clk / hclk_max;
+ if (hdiv < 1)
+ hdiv = 1;
+
+ cfg->freq.hclk = hclk = armdiv_clk / hdiv;
+
+ /* set dvs depending on whether we reached armclk or not. */
+ cfg->divs.dvs = dvs = armclk < armdiv_clk;
+
+ /* update the actual armclk we achieved. */
+ cfg->freq.armclk = dvs ? hclk : armdiv_clk;
+
+ s3c_freq_dbg("%s: armclk %lu, hclk %lu, armdiv %d, hdiv %d, dvs %d\n",
+ __func__, armclk, hclk, armdiv, hdiv, cfg->divs.dvs);
+
+ if (hdiv > 4)
+ goto invalid;
+
+ pdiv = (hclk > cfg->max.pclk) ? 2 : 1;
+
+ if ((hclk / pdiv) > cfg->max.pclk)
+ pdiv++;
+
+ cfg->freq.pclk = hclk / pdiv;
+
+ s3c_freq_dbg("%s: pdiv %d\n", __func__, pdiv);
+
+ if (pdiv > 2)
+ goto invalid;
+
+ pdiv *= hdiv;
+
+ /* store the result, and then return */
+
+ cfg->divs.h_divisor = hdiv * armdiv;
+ cfg->divs.p_divisor = pdiv * armdiv;
+
+ return 0;
+
+ invalid:
+ return -EINVAL;
+}
+
+static void s3c2412_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
+{
+ unsigned long clkdiv;
+ unsigned long olddiv;
+
+ olddiv = clkdiv = __raw_readl(S3C2410_CLKDIVN);
+
+ /* clear off current clock info */
+
+ clkdiv &= ~S3C2412_CLKDIVN_ARMDIVN;
+ clkdiv &= ~S3C2412_CLKDIVN_HDIVN_MASK;
+ clkdiv &= ~S3C2412_CLKDIVN_PDIVN;
+
+ if (cfg->divs.arm_divisor == 2)
+ clkdiv |= S3C2412_CLKDIVN_ARMDIVN;
+
+ clkdiv |= ((cfg->divs.h_divisor / cfg->divs.arm_divisor) - 1);
+
+ if (cfg->divs.p_divisor != cfg->divs.h_divisor)
+ clkdiv |= S3C2412_CLKDIVN_PDIVN;
+
+ s3c_freq_dbg("%s: div %08lx => %08lx\n", __func__, olddiv, clkdiv);
+ __raw_writel(clkdiv, S3C2410_CLKDIVN);
+
+ clk_set_parent(armclk, cfg->divs.dvs ? hclk : fclk);
+}
+
+static void s3c2412_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg)
+{
+ struct s3c_cpufreq_board *board = cfg->board;
+ unsigned long refresh;
+
+ s3c_freq_dbg("%s: refresh %u ns, hclk %lu\n", __func__,
+ board->refresh, cfg->freq.hclk);
+
+ /* Reduce both the refresh time (in ns) and the frequency (in MHz)
+ * by 10 each to ensure that we do not overflow 32 bit numbers. This
+ * should work for HCLK up to 133MHz and refresh period up to 30usec.
+ */
+
+ refresh = (board->refresh / 10);
+ refresh *= (cfg->freq.hclk / 100);
+ refresh /= (1 * 1000 * 1000); /* 10^6 */
+
+ s3c_freq_dbg("%s: setting refresh 0x%08lx\n", __func__, refresh);
+ __raw_writel(refresh, S3C2412_REFRESH);
+}
+
+/* set the default cpu frequency information, based on an 200MHz part
+ * as we have no other way of detecting the speed rating in software.
+ */
+
+static struct s3c_cpufreq_info s3c2412_cpufreq_info = {
+ .max = {
+ .fclk = 200000000,
+ .hclk = 100000000,
+ .pclk = 50000000,
+ },
+
+ .latency = 5000000, /* 5ms */
+
+ .locktime_m = 150,
+ .locktime_u = 150,
+ .locktime_bits = 16,
+
+ .name = "s3c2412",
+ .set_refresh = s3c2412_cpufreq_setrefresh,
+ .set_divs = s3c2412_cpufreq_setdivs,
+ .calc_divs = s3c2412_cpufreq_calcdivs,
+
+ .calc_iotiming = s3c2412_iotiming_calc,
+ .set_iotiming = s3c2412_iotiming_set,
+ .get_iotiming = s3c2412_iotiming_get,
+
+ .resume_clocks = s3c2412_setup_clocks,
+
+ .debug_io_show = s3c_cpufreq_debugfs_call(s3c2412_iotiming_debugfs),
+};
+
+static int s3c2412_cpufreq_add(struct sys_device *sysdev)
+{
+ unsigned long fclk_rate;
+
+ hclk = clk_get(NULL, "hclk");
+ if (IS_ERR(hclk)) {
+ printk(KERN_ERR "%s: cannot find hclk clock\n", __func__);
+ return -ENOENT;
+ }
+
+ fclk = clk_get(NULL, "fclk");
+ if (IS_ERR(fclk)) {
+ printk(KERN_ERR "%s: cannot find fclk clock\n", __func__);
+ goto err_fclk;
+ }
+
+ fclk_rate = clk_get_rate(fclk);
+ if (fclk_rate > 200000000) {
+ printk(KERN_INFO
+ "%s: fclk %ld MHz, assuming 266MHz capable part\n",
+ __func__, fclk_rate / 1000000);
+ s3c2412_cpufreq_info.max.fclk = 266000000;
+ s3c2412_cpufreq_info.max.hclk = 133000000;
+ s3c2412_cpufreq_info.max.pclk = 66000000;
+ }
+
+ armclk = clk_get(NULL, "armclk");
+ if (IS_ERR(armclk)) {
+ printk(KERN_ERR "%s: cannot find arm clock\n", __func__);
+ goto err_armclk;
+ }
+
+ xtal = clk_get(NULL, "xtal");
+ if (IS_ERR(xtal)) {
+ printk(KERN_ERR "%s: cannot find xtal clock\n", __func__);
+ goto err_xtal;
+ }
+
+ return s3c_cpufreq_register(&s3c2412_cpufreq_info);
+
+err_xtal:
+ clk_put(armclk);
+err_armclk:
+ clk_put(fclk);
+err_fclk:
+ clk_put(hclk);
+
+ return -ENOENT;
+}
+
+static struct sysdev_driver s3c2412_cpufreq_driver = {
+ .add = s3c2412_cpufreq_add,
+};
+
+static int s3c2412_cpufreq_init(void)
+{
+ return sysdev_driver_register(&s3c2412_sysclass,
+ &s3c2412_cpufreq_driver);
+}
+
+arch_initcall(s3c2412_cpufreq_init);
diff --git a/arch/arm/mach-s3c2412/s3c2412.c b/arch/arm/mach-s3c2412/s3c2412.c
index 5b5aba69ec3..bef39f77729 100644
--- a/arch/arm/mach-s3c2412/s3c2412.c
+++ b/arch/arm/mach-s3c2412/s3c2412.c
@@ -69,6 +69,18 @@ static struct map_desc s3c2412_iodesc[] __initdata = {
IODESC_ENT(CLKPWR),
IODESC_ENT(TIMER),
IODESC_ENT(WATCHDOG),
+ {
+ .virtual = (unsigned long)S3C2412_VA_SSMC,
+ .pfn = __phys_to_pfn(S3C2412_PA_SSMC),
+ .length = SZ_1M,
+ .type = MT_DEVICE,
+ },
+ {
+ .virtual = (unsigned long)S3C2412_VA_EBI,
+ .pfn = __phys_to_pfn(S3C2412_PA_EBI),
+ .length = SZ_1M,
+ .type = MT_DEVICE,
+ },
};
/* uart registration process */
diff --git a/arch/arm/mach-s3c2440/Kconfig b/arch/arm/mach-s3c2440/Kconfig
index 8cfeaec3730..8ae1b288f7f 100644
--- a/arch/arm/mach-s3c2440/Kconfig
+++ b/arch/arm/mach-s3c2440/Kconfig
@@ -33,6 +33,7 @@ config MACH_ANUBIS
select PM_SIMTEC if PM
select HAVE_PATA_PLATFORM
select S3C24XX_GPIO_EXTRA64
+ select S3C2440_XTAL_12000000
select S3C_DEV_USB_HOST
help
Say Y here if you are using the Simtec Electronics ANUBIS
@@ -44,6 +45,8 @@ config MACH_OSIRIS
select S3C24XX_DCLK
select PM_SIMTEC if PM
select S3C24XX_GPIO_EXTRA128
+ select S3C2440_XTAL_12000000
+ select S3C2410_IOTIMING if S3C2440_CPUFREQ
select S3C_DEV_USB_HOST
help
Say Y here if you are using the Simtec IM2440D20 module, also
@@ -52,6 +55,7 @@ config MACH_OSIRIS
config MACH_RX3715
bool "HP iPAQ rx3715"
select CPU_S3C2440
+ select S3C2440_XTAL_16934400
select PM_H1940 if PM
help
Say Y here if you are using the HP iPAQ rx3715.
@@ -59,6 +63,7 @@ config MACH_RX3715
config ARCH_S3C2440
bool "SMDK2440"
select CPU_S3C2440
+ select S3C2440_XTAL_16934400
select MACH_SMDK
select S3C_DEV_USB_HOST
help
@@ -67,6 +72,7 @@ config ARCH_S3C2440
config MACH_NEXCODER_2440
bool "NexVision NEXCODER 2440 Light Board"
select CPU_S3C2440
+ select S3C2440_XTAL_12000000
select S3C_DEV_USB_HOST
help
Say Y here if you are using the Nex Vision NEXCODER 2440 Light Board
@@ -75,6 +81,7 @@ config SMDK2440_CPU2440
bool "SMDK2440 with S3C2440 CPU module"
depends on ARCH_S3C2440
default y if ARCH_S3C2440
+ select S3C2440_XTAL_16934400
select CPU_S3C2440
config MACH_AT2440EVB
diff --git a/arch/arm/mach-s3c2440/mach-osiris.c b/arch/arm/mach-s3c2440/mach-osiris.c
index cba064b49a6..2105a41281a 100644
--- a/arch/arm/mach-s3c2440/mach-osiris.c
+++ b/arch/arm/mach-s3c2440/mach-osiris.c
@@ -34,6 +34,7 @@
#include <asm/irq.h>
#include <asm/mach-types.h>
+#include <plat/cpu-freq.h>
#include <plat/regs-serial.h>
#include <mach/regs-gpio.h>
#include <mach/regs-mem.h>
@@ -351,6 +352,12 @@ static struct clk *osiris_clocks[] __initdata = {
&s3c24xx_uclk,
};
+static struct s3c_cpufreq_board __initdata osiris_cpufreq = {
+ .refresh = 7800, /* refresh period is 7.8usec */
+ .auto_io = 1,
+ .need_io = 1,
+};
+
static void __init osiris_map_io(void)
{
unsigned long flags;
@@ -402,6 +409,8 @@ static void __init osiris_init(void)
s3c_i2c0_set_platdata(NULL);
+ s3c_cpufreq_setboard(&osiris_cpufreq);
+
i2c_register_board_info(0, osiris_i2c_devs,
ARRAY_SIZE(osiris_i2c_devs));
diff --git a/arch/arm/mach-s3c24a0/include/mach/map.h b/arch/arm/mach-s3c24a0/include/mach/map.h
index a01132717e3..79e4d93ea2b 100644
--- a/arch/arm/mach-s3c24a0/include/mach/map.h
+++ b/arch/arm/mach-s3c24a0/include/mach/map.h
@@ -81,5 +81,6 @@
#define S3C_PA_UART S3C24A0_PA_UART
#define S3C_PA_IIC S3C24A0_PA_IIC
+#define S3C_PA_NAND S3C24XX_PA_NAND
#endif /* __ASM_ARCH_24A0_MAP_H */
diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h
index 5057d9948d3..fc8b223bad4 100644
--- a/arch/arm/mach-s3c6400/include/mach/map.h
+++ b/arch/arm/mach-s3c6400/include/mach/map.h
@@ -38,18 +38,21 @@
#define S3C_VA_UART2 S3C_VA_UARTx(2)
#define S3C_VA_UART3 S3C_VA_UARTx(3)
+#define S3C64XX_PA_NAND (0x70200000)
#define S3C64XX_PA_FB (0x77100000)
#define S3C64XX_PA_USB_HSOTG (0x7C000000)
#define S3C64XX_PA_WATCHDOG (0x7E004000)
#define S3C64XX_PA_SYSCON (0x7E00F000)
+#define S3C64XX_PA_AC97 (0x7F001000)
#define S3C64XX_PA_IIS0 (0x7F002000)
#define S3C64XX_PA_IIS1 (0x7F003000)
#define S3C64XX_PA_TIMER (0x7F006000)
#define S3C64XX_PA_IIC0 (0x7F004000)
+#define S3C64XX_PA_IISV4 (0x7F00D000)
#define S3C64XX_PA_IIC1 (0x7F00F000)
#define S3C64XX_PA_GPIO (0x7F008000)
-#define S3C64XX_VA_GPIO S3C_ADDR(0x00500000)
+#define S3C64XX_VA_GPIO S3C_ADDR_CPU(0x00000000)
#define S3C64XX_SZ_GPIO SZ_4K
#define S3C64XX_PA_SDRAM (0x50000000)
@@ -57,7 +60,7 @@
#define S3C64XX_PA_VIC1 (0x71300000)
#define S3C64XX_PA_MODEM (0x74108000)
-#define S3C64XX_VA_MODEM S3C_ADDR(0x00600000)
+#define S3C64XX_VA_MODEM S3C_ADDR_CPU(0x00100000)
#define S3C64XX_PA_USBHOST (0x74300000)
@@ -72,6 +75,7 @@
#define S3C_PA_HSMMC2 S3C64XX_PA_HSMMC2
#define S3C_PA_IIC S3C64XX_PA_IIC0
#define S3C_PA_IIC1 S3C64XX_PA_IIC1
+#define S3C_PA_NAND S3C64XX_PA_NAND
#define S3C_PA_FB S3C64XX_PA_FB
#define S3C_PA_USBHOST S3C64XX_PA_USBHOST
#define S3C_PA_USB_HSOTG S3C64XX_PA_USB_HSOTG
diff --git a/arch/arm/mach-s3c6400/s3c6400.c b/arch/arm/mach-s3c6400/s3c6400.c
index 1ece887d90b..b42bdd0f213 100644
--- a/arch/arm/mach-s3c6400/s3c6400.c
+++ b/arch/arm/mach-s3c6400/s3c6400.c
@@ -48,6 +48,8 @@ void __init s3c6400_map_io(void)
/* the i2c devices are directly compatible with s3c2440 */
s3c_i2c0_setname("s3c2440-i2c");
+
+ s3c_device_nand.name = "s3c6400-nand";
}
void __init s3c6400_init_clocks(int xtal)
diff --git a/arch/arm/mach-s3c6410/Kconfig b/arch/arm/mach-s3c6410/Kconfig
index e63aac7f4e5..f9d0f09f976 100644
--- a/arch/arm/mach-s3c6410/Kconfig
+++ b/arch/arm/mach-s3c6410/Kconfig
@@ -97,3 +97,13 @@ config MACH_NCP
select S3C64XX_SETUP_I2C1
help
Machine support for the Samsung NCP
+
+config MACH_HMT
+ bool "Airgoo HMT"
+ select CPU_S3C6410
+ select S3C_DEV_FB
+ select S3C_DEV_USB_HOST
+ select S3C64XX_SETUP_FB_24BPP
+ select HAVE_PWM
+ help
+ Machine support for the Airgoo HMT
diff --git a/arch/arm/mach-s3c6410/Makefile b/arch/arm/mach-s3c6410/Makefile
index 6f9deac8861..3e48c3dbf97 100644
--- a/arch/arm/mach-s3c6410/Makefile
+++ b/arch/arm/mach-s3c6410/Makefile
@@ -23,5 +23,4 @@ obj-$(CONFIG_S3C6410_SETUP_SDHCI) += setup-sdhci.o
obj-$(CONFIG_MACH_ANW6410) += mach-anw6410.o
obj-$(CONFIG_MACH_SMDK6410) += mach-smdk6410.o
obj-$(CONFIG_MACH_NCP) += mach-ncp.o
-
-
+obj-$(CONFIG_MACH_HMT) += mach-hmt.o
diff --git a/arch/arm/mach-s3c6410/cpu.c b/arch/arm/mach-s3c6410/cpu.c
index ade904de889..9b67c663d9d 100644
--- a/arch/arm/mach-s3c6410/cpu.c
+++ b/arch/arm/mach-s3c6410/cpu.c
@@ -62,6 +62,8 @@ void __init s3c6410_map_io(void)
/* the i2c devices are directly compatible with s3c2440 */
s3c_i2c0_setname("s3c2440-i2c");
s3c_i2c1_setname("s3c2440-i2c");
+
+ s3c_device_nand.name = "s3c6400-nand";
}
void __init s3c6410_init_clocks(int xtal)
diff --git a/arch/arm/mach-s3c6410/mach-hmt.c b/arch/arm/mach-s3c6410/mach-hmt.c
new file mode 100644
index 00000000000..c5741056193
--- /dev/null
+++ b/arch/arm/mach-s3c6410/mach-hmt.c
@@ -0,0 +1,276 @@
+/* mach-hmt.c - Platform code for Airgoo HMT
+ *
+ * Copyright 2009 Peter Korsgaard <jacmet@sunsite.dk>
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/leds.h>
+#include <linux/pwm_backlight.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-fb.h>
+#include <mach/map.h>
+
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include <plat/regs-serial.h>
+#include <plat/iic.h>
+#include <plat/fb.h>
+#include <plat/nand.h>
+
+#include <plat/s3c6410.h>
+#include <plat/clock.h>
+#include <plat/devs.h>
+#include <plat/cpu.h>
+
+#define UCON S3C2410_UCON_DEFAULT
+#define ULCON (S3C2410_LCON_CS8 | S3C2410_LCON_PNONE)
+#define UFCON (S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE)
+
+static struct s3c2410_uartcfg hmt_uartcfgs[] __initdata = {
+ [0] = {
+ .hwport = 0,
+ .flags = 0,
+ .ucon = UCON,
+ .ulcon = ULCON,
+ .ufcon = UFCON,
+ },
+ [1] = {
+ .hwport = 1,
+ .flags = 0,
+ .ucon = UCON,
+ .ulcon = ULCON,
+ .ufcon = UFCON,
+ },
+ [2] = {
+ .hwport = 2,
+ .flags = 0,
+ .ucon = UCON,
+ .ulcon = ULCON,
+ .ufcon = UFCON,
+ },
+};
+
+static int hmt_bl_init(struct device *dev)
+{
+ int ret;
+
+ ret = gpio_request(S3C64XX_GPB(4), "lcd backlight enable");
+ if (!ret)
+ ret = gpio_direction_output(S3C64XX_GPB(4), 0);
+
+ return ret;
+}
+
+static int hmt_bl_notify(int brightness)
+{
+ /*
+ * translate from CIELUV/CIELAB L*->brightness, E.G. from
+ * perceived luminance to light output. Assumes range 0..25600
+ */
+ if (brightness < 0x800) {
+ /* Y = Yn * L / 903.3 */
+ brightness = (100*256 * brightness + 231245/2) / 231245;
+ } else {
+ /* Y = Yn * ((L + 16) / 116 )^3 */
+ int t = (brightness*4 + 16*1024 + 58)/116;
+ brightness = 25 * ((t * t * t + 0x100000/2) / 0x100000);
+ }
+
+ gpio_set_value(S3C64XX_GPB(4), brightness);
+
+ return brightness;
+}
+
+static void hmt_bl_exit(struct device *dev)
+{
+ gpio_free(S3C64XX_GPB(4));
+}
+
+static struct platform_pwm_backlight_data hmt_backlight_data = {
+ .pwm_id = 1,
+ .max_brightness = 100 * 256,
+ .dft_brightness = 40 * 256,
+ .pwm_period_ns = 1000000000 / (100 * 256 * 20),
+ .init = hmt_bl_init,
+ .notify = hmt_bl_notify,
+ .exit = hmt_bl_exit,
+
+};
+
+static struct platform_device hmt_backlight_device = {
+ .name = "pwm-backlight",
+ .dev = {
+ .parent = &s3c_device_timer[1].dev,
+ .platform_data = &hmt_backlight_data,
+ },
+};
+
+static struct s3c_fb_pd_win hmt_fb_win0 = {
+ .win_mode = {
+ .pixclock = 41094,
+ .left_margin = 8,
+ .right_margin = 13,
+ .upper_margin = 7,
+ .lower_margin = 5,
+ .hsync_len = 3,
+ .vsync_len = 1,
+ .xres = 800,
+ .yres = 480,
+ },
+ .max_bpp = 32,
+ .default_bpp = 16,
+};
+
+/* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */
+static struct s3c_fb_platdata hmt_lcd_pdata __initdata = {
+ .setup_gpio = s3c64xx_fb_gpio_setup_24bpp,
+ .win[0] = &hmt_fb_win0,
+ .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB,
+ .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC,
+};
+
+static struct mtd_partition hmt_nand_part[] = {
+ [0] = {
+ .name = "uboot",
+ .size = SZ_512K,
+ .offset = 0,
+ },
+ [1] = {
+ .name = "uboot-env1",
+ .size = SZ_256K,
+ .offset = SZ_512K,
+ },
+ [2] = {
+ .name = "uboot-env2",
+ .size = SZ_256K,
+ .offset = SZ_512K + SZ_256K,
+ },
+ [3] = {
+ .name = "kernel",
+ .size = SZ_2M,
+ .offset = SZ_1M,
+ },
+ [4] = {
+ .name = "rootfs",
+ .size = MTDPART_SIZ_FULL,
+ .offset = SZ_1M + SZ_2M,
+ },
+};
+
+static struct s3c2410_nand_set hmt_nand_sets[] = {
+ [0] = {
+ .name = "nand",
+ .nr_chips = 1,
+ .nr_partitions = ARRAY_SIZE(hmt_nand_part),
+ .partitions = hmt_nand_part,
+ },
+};
+
+static struct s3c2410_platform_nand hmt_nand_info = {
+ .tacls = 25,
+ .twrph0 = 55,
+ .twrph1 = 40,
+ .nr_sets = ARRAY_SIZE(hmt_nand_sets),
+ .sets = hmt_nand_sets,
+};
+
+static struct gpio_led hmt_leds[] = {
+ { /* left function keys */
+ .name = "left:blue",
+ .gpio = S3C64XX_GPO(12),
+ .default_trigger = "default-on",
+ },
+ { /* right function keys - red */
+ .name = "right:red",
+ .gpio = S3C64XX_GPO(13),
+ },
+ { /* right function keys - green */
+ .name = "right:green",
+ .gpio = S3C64XX_GPO(14),
+ },
+ { /* right function keys - blue */
+ .name = "right:blue",
+ .gpio = S3C64XX_GPO(15),
+ .default_trigger = "default-on",
+ },
+};
+
+static struct gpio_led_platform_data hmt_led_data = {
+ .num_leds = ARRAY_SIZE(hmt_leds),
+ .leds = hmt_leds,
+};
+
+static struct platform_device hmt_leds_device = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev.platform_data = &hmt_led_data,
+};
+
+static struct map_desc hmt_iodesc[] = {};
+
+static struct platform_device *hmt_devices[] __initdata = {
+ &s3c_device_i2c0,
+ &s3c_device_nand,
+ &s3c_device_fb,
+ &s3c_device_usb,
+ &s3c_device_timer[1],
+ &hmt_backlight_device,
+ &hmt_leds_device,
+};
+
+static void __init hmt_map_io(void)
+{
+ s3c64xx_init_io(hmt_iodesc, ARRAY_SIZE(hmt_iodesc));
+ s3c24xx_init_clocks(12000000);
+ s3c24xx_init_uarts(hmt_uartcfgs, ARRAY_SIZE(hmt_uartcfgs));
+}
+
+static void __init hmt_machine_init(void)
+{
+ s3c_i2c0_set_platdata(NULL);
+ s3c_fb_set_platdata(&hmt_lcd_pdata);
+ s3c_device_nand.dev.platform_data = &hmt_nand_info;
+
+ gpio_request(S3C64XX_GPC(7), "usb power");
+ gpio_direction_output(S3C64XX_GPC(7), 0);
+ gpio_request(S3C64XX_GPM(0), "usb power");
+ gpio_direction_output(S3C64XX_GPM(0), 1);
+ gpio_request(S3C64XX_GPK(7), "usb power");
+ gpio_direction_output(S3C64XX_GPK(7), 1);
+ gpio_request(S3C64XX_GPF(13), "usb power");
+ gpio_direction_output(S3C64XX_GPF(13), 1);
+
+ platform_add_devices(hmt_devices, ARRAY_SIZE(hmt_devices));
+}
+
+MACHINE_START(HMT, "Airgoo-HMT")
+ /* Maintainer: Peter Korsgaard <jacmet@sunsite.dk> */
+ .phys_io = S3C_PA_UART & 0xfff00000,
+ .io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
+ .boot_params = S3C64XX_PA_SDRAM + 0x100,
+ .init_irq = s3c6410_init_irq,
+ .map_io = hmt_map_io,
+ .init_machine = hmt_machine_init,
+ .timer = &s3c24xx_timer,
+MACHINE_END
diff --git a/arch/arm/mach-s3c6410/mach-ncp.c b/arch/arm/mach-s3c6410/mach-ncp.c
index 6030636f854..55e9bbfaf68 100644
--- a/arch/arm/mach-s3c6410/mach-ncp.c
+++ b/arch/arm/mach-s3c6410/mach-ncp.c
@@ -79,7 +79,7 @@ static struct platform_device *ncp_devices[] __initdata = {
&s3c_device_i2c0,
};
-struct map_desc ncp_iodesc[] = {};
+static struct map_desc ncp_iodesc[] __initdata = {};
static void __init ncp_map_io(void)
{
diff --git a/arch/arm/mach-s3c6410/mach-smdk6410.c b/arch/arm/mach-s3c6410/mach-smdk6410.c
index bc9a7dea567..ea51dbe76e3 100644
--- a/arch/arm/mach-s3c6410/mach-smdk6410.c
+++ b/arch/arm/mach-s3c6410/mach-smdk6410.c
@@ -65,16 +65,30 @@ static struct s3c2410_uartcfg smdk6410_uartcfgs[] __initdata = {
[0] = {
.hwport = 0,
.flags = 0,
- .ucon = 0x3c5,
- .ulcon = 0x03,
- .ufcon = 0x51,
+ .ucon = UCON,
+ .ulcon = ULCON,
+ .ufcon = UFCON,
},
[1] = {
.hwport = 1,
.flags = 0,
- .ucon = 0x3c5,
- .ulcon = 0x03,
- .ufcon = 0x51,
+ .ucon = UCON,
+ .ulcon = ULCON,
+ .ufcon = UFCON,
+ },
+ [2] = {
+ .hwport = 2,
+ .flags = 0,
+ .ucon = UCON,
+ .ulcon = ULCON,
+ .ufcon = UFCON,
+ },
+ [3] = {
+ .hwport = 3,
+ .flags = 0,
+ .ucon = UCON,
+ .ulcon = ULCON,
+ .ufcon = UFCON,
},
};
diff --git a/arch/arm/mach-s5pc100/Kconfig b/arch/arm/mach-s5pc100/Kconfig
new file mode 100644
index 00000000000..b1a4ba50441
--- /dev/null
+++ b/arch/arm/mach-s5pc100/Kconfig
@@ -0,0 +1,22 @@
+# arch/arm/mach-s5pc100/Kconfig
+#
+# Copyright 2009 Samsung Electronics Co.
+# Byungho Min <bhmin@samsung.com>
+#
+# Licensed under GPLv2
+
+# Configuration options for the S5PC100 CPU
+
+config CPU_S5PC100
+ bool
+ select CPU_S5PC100_INIT
+ select CPU_S5PC100_CLOCK
+ help
+ Enable S5PC100 CPU support
+
+config MACH_SMDKC100
+ bool "SMDKC100"
+ select CPU_S5PC100
+ select S5PC1XX_SETUP_I2C1
+ help
+ Machine support for the Samsung SMDKC100
diff --git a/arch/arm/mach-s5pc100/Makefile b/arch/arm/mach-s5pc100/Makefile
new file mode 100644
index 00000000000..afc89b381d7
--- /dev/null
+++ b/arch/arm/mach-s5pc100/Makefile
@@ -0,0 +1,17 @@
+# arch/arm/mach-s5pc100/Makefile
+#
+# Copyright 2009 Samsung Electronics Co.
+#
+# Licensed under GPLv2
+
+obj-y :=
+obj-m :=
+obj-n :=
+obj- :=
+
+# Core support for S5PC100 system
+
+obj-$(CONFIG_CPU_S5PC100) += cpu.o
+
+# machine support
+obj-$(CONFIG_MACH_SMDKC100) += mach-smdkc100.o
diff --git a/arch/arm/mach-s5pc100/Makefile.boot b/arch/arm/mach-s5pc100/Makefile.boot
new file mode 100644
index 00000000000..ff90aa13bd6
--- /dev/null
+++ b/arch/arm/mach-s5pc100/Makefile.boot
@@ -0,0 +1,2 @@
+ zreladdr-y := 0x20008000
+params_phys-y := 0x20000100
diff --git a/arch/arm/mach-s5pc100/cpu.c b/arch/arm/mach-s5pc100/cpu.c
new file mode 100644
index 00000000000..0e718890da3
--- /dev/null
+++ b/arch/arm/mach-s5pc100/cpu.c
@@ -0,0 +1,97 @@
+/* linux/arch/arm/mach-s5pc100/cpu.c
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * Based on mach-s3c6410/cpu.c
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/sysdev.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/map.h>
+#include <asm/irq.h>
+
+#include <plat/cpu-freq.h>
+#include <plat/regs-serial.h>
+
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/clock.h>
+#include <plat/sdhci.h>
+#include <plat/iic-core.h>
+#include <plat/s5pc100.h>
+
+/* Initial IO mappings */
+
+static struct map_desc s5pc100_iodesc[] __initdata = {
+};
+
+/* s5pc100_map_io
+ *
+ * register the standard cpu IO areas
+*/
+
+void __init s5pc100_map_io(void)
+{
+ iotable_init(s5pc100_iodesc, ARRAY_SIZE(s5pc100_iodesc));
+
+ /* initialise device information early */
+}
+
+void __init s5pc100_init_clocks(int xtal)
+{
+ printk(KERN_DEBUG "%s: initialising clocks\n", __func__);
+ s3c24xx_register_baseclocks(xtal);
+ s5pc1xx_register_clocks();
+ s5pc100_register_clocks();
+ s5pc100_setup_clocks();
+}
+
+void __init s5pc100_init_irq(void)
+{
+ u32 vic_valid[] = {~0, ~0, ~0};
+
+ /* VIC0, VIC1, and VIC2 are fully populated. */
+ s5pc1xx_init_irq(vic_valid, ARRAY_SIZE(vic_valid));
+}
+
+struct sysdev_class s5pc100_sysclass = {
+ .name = "s5pc100-core",
+};
+
+static struct sys_device s5pc100_sysdev = {
+ .cls = &s5pc100_sysclass,
+};
+
+static int __init s5pc100_core_init(void)
+{
+ return sysdev_class_register(&s5pc100_sysclass);
+}
+
+core_initcall(s5pc100_core_init);
+
+int __init s5pc100_init(void)
+{
+ printk(KERN_DEBUG "S5PC100: Initialising architecture\n");
+
+ return sysdev_register(&s5pc100_sysdev);
+}
diff --git a/arch/arm/mach-s5pc100/include/mach/debug-macro.S b/arch/arm/mach-s5pc100/include/mach/debug-macro.S
new file mode 100644
index 00000000000..9d142ccf654
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/debug-macro.S
@@ -0,0 +1,38 @@
+/* arch/arm/mach-s5pc100/include/mach/debug-macro.S
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ *
+ * Based on mach-s3c6400/include/mach/debug-macro.S
+ *
+ * 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.
+*/
+
+/* pull in the relevant register and map files. */
+
+#include <mach/map.h>
+#include <plat/regs-serial.h>
+
+ /* note, for the boot process to work we have to keep the UART
+ * virtual address aligned to an 1MiB boundary for the L1
+ * mapping the head code makes. We keep the UART virtual address
+ * aligned and add in the offset when we load the value here.
+ */
+
+ .macro addruart, rx
+ mrc p15, 0, \rx, c1, c0
+ tst \rx, #1
+ ldreq \rx, = S3C_PA_UART
+ ldrne \rx, = (S3C_VA_UART + S3C_PA_UART & 0xfffff)
+ add \rx, \rx, #(0x400 * CONFIG_DEBUG_S3C_UART)
+ .endm
+
+/* include the reset of the code which will do the work, we're only
+ * compiling for a single cpu processor type so the default of s3c2440
+ * will be fine with us.
+ */
+
+#include <plat/debug-macro.S>
diff --git a/arch/arm/mach-s5pc100/include/mach/entry-macro.S b/arch/arm/mach-s5pc100/include/mach/entry-macro.S
new file mode 100644
index 00000000000..67131939e62
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/entry-macro.S
@@ -0,0 +1,50 @@
+/* arch/arm/mach-s5pc100/include/mach/entry-macro.S
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * Based on mach-s3c6400/include/mach/entry-macro.S
+ *
+ * Low-level IRQ helper macros for the Samsung S5PC1XX series
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+*/
+
+#include <asm/hardware/vic.h>
+#include <mach/map.h>
+#include <plat/irqs.h>
+
+ .macro disable_fiq
+ .endm
+
+ .macro get_irqnr_preamble, base, tmp
+ ldr \base, =S3C_VA_VIC0
+ .endm
+
+ .macro arch_ret_to_user, tmp1, tmp2
+ .endm
+
+ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
+
+ @ check the vic0
+ mov \irqnr, # S3C_IRQ_OFFSET + 31
+ ldr \irqstat, [ \base, # VIC_IRQ_STATUS ]
+ teq \irqstat, #0
+
+ @ otherwise try vic1
+ addeq \tmp, \base, #(S3C_VA_VIC1 - S3C_VA_VIC0)
+ addeq \irqnr, \irqnr, #32
+ ldreq \irqstat, [ \tmp, # VIC_IRQ_STATUS ]
+ teqeq \irqstat, #0
+
+ @ otherwise try vic2
+ addeq \tmp, \base, #(S3C_VA_VIC2 - S3C_VA_VIC0)
+ addeq \irqnr, \irqnr, #32
+ ldreq \irqstat, [ \tmp, # VIC_IRQ_STATUS ]
+ teqeq \irqstat, #0
+
+ clzne \irqstat, \irqstat
+ subne \irqnr, \irqnr, \irqstat
+ .endm
diff --git a/arch/arm/mach-s5pc100/include/mach/gpio-core.h b/arch/arm/mach-s5pc100/include/mach/gpio-core.h
new file mode 100644
index 00000000000..ad28d8ec8a7
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/gpio-core.h
@@ -0,0 +1,21 @@
+/* arch/arm/mach-s5pc100/include/mach/gpio-core.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC100 - GPIO core support
+ *
+ * Based on mach-s3c6400/include/mach/gpio-core.h
+ *
+ * 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_ARCH_GPIO_CORE_H
+#define __ASM_ARCH_GPIO_CORE_H __FILE__
+
+/* currently we just include the platform support */
+#include <plat/gpio-core.h>
+
+#endif /* __ASM_ARCH_GPIO_CORE_H */
diff --git a/arch/arm/mach-s5pc100/include/mach/gpio.h b/arch/arm/mach-s5pc100/include/mach/gpio.h
new file mode 100644
index 00000000000..c74fc93d7d1
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/gpio.h
@@ -0,0 +1,146 @@
+/* arch/arm/mach-s5pc100/include/mach/gpio.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC100 - GPIO lib support
+ *
+ * Base on mach-s3c6400/include/mach/gpio.h
+ *
+ * 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.
+*/
+
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
+#define gpio_to_irq __gpio_to_irq
+
+/* GPIO bank sizes */
+#define S5PC1XX_GPIO_A0_NR (8)
+#define S5PC1XX_GPIO_A1_NR (5)
+#define S5PC1XX_GPIO_B_NR (8)
+#define S5PC1XX_GPIO_C_NR (5)
+#define S5PC1XX_GPIO_D_NR (7)
+#define S5PC1XX_GPIO_E0_NR (8)
+#define S5PC1XX_GPIO_E1_NR (6)
+#define S5PC1XX_GPIO_F0_NR (8)
+#define S5PC1XX_GPIO_F1_NR (8)
+#define S5PC1XX_GPIO_F2_NR (8)
+#define S5PC1XX_GPIO_F3_NR (4)
+#define S5PC1XX_GPIO_G0_NR (8)
+#define S5PC1XX_GPIO_G1_NR (3)
+#define S5PC1XX_GPIO_G2_NR (7)
+#define S5PC1XX_GPIO_G3_NR (7)
+#define S5PC1XX_GPIO_H0_NR (8)
+#define S5PC1XX_GPIO_H1_NR (8)
+#define S5PC1XX_GPIO_H2_NR (8)
+#define S5PC1XX_GPIO_H3_NR (8)
+#define S5PC1XX_GPIO_I_NR (8)
+#define S5PC1XX_GPIO_J0_NR (8)
+#define S5PC1XX_GPIO_J1_NR (5)
+#define S5PC1XX_GPIO_J2_NR (8)
+#define S5PC1XX_GPIO_J3_NR (8)
+#define S5PC1XX_GPIO_J4_NR (4)
+#define S5PC1XX_GPIO_K0_NR (8)
+#define S5PC1XX_GPIO_K1_NR (6)
+#define S5PC1XX_GPIO_K2_NR (8)
+#define S5PC1XX_GPIO_K3_NR (8)
+#define S5PC1XX_GPIO_MP00_NR (8)
+#define S5PC1XX_GPIO_MP01_NR (8)
+#define S5PC1XX_GPIO_MP02_NR (8)
+#define S5PC1XX_GPIO_MP03_NR (8)
+#define S5PC1XX_GPIO_MP04_NR (5)
+
+/* GPIO bank numbes */
+
+/* CONFIG_S3C_GPIO_SPACE allows the user to select extra
+ * space for debugging purposes so that any accidental
+ * change from one gpio bank to another can be caught.
+*/
+
+#define S5PC1XX_GPIO_NEXT(__gpio) \
+ ((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)
+
+enum s3c_gpio_number {
+ S5PC1XX_GPIO_A0_START = 0,
+ S5PC1XX_GPIO_A1_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_A0),
+ S5PC1XX_GPIO_B_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_A1),
+ S5PC1XX_GPIO_C_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_B),
+ S5PC1XX_GPIO_D_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_C),
+ S5PC1XX_GPIO_E0_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_D),
+ S5PC1XX_GPIO_E1_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_E0),
+ S5PC1XX_GPIO_F0_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_E1),
+ S5PC1XX_GPIO_F1_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_F0),
+ S5PC1XX_GPIO_F2_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_F1),
+ S5PC1XX_GPIO_F3_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_F2),
+ S5PC1XX_GPIO_G0_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_F3),
+ S5PC1XX_GPIO_G1_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_G0),
+ S5PC1XX_GPIO_G2_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_G1),
+ S5PC1XX_GPIO_G3_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_G2),
+ S5PC1XX_GPIO_H0_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_G3),
+ S5PC1XX_GPIO_H1_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_H0),
+ S5PC1XX_GPIO_H2_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_H1),
+ S5PC1XX_GPIO_H3_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_H2),
+ S5PC1XX_GPIO_I_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_H3),
+ S5PC1XX_GPIO_J0_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_I),
+ S5PC1XX_GPIO_J1_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_J0),
+ S5PC1XX_GPIO_J2_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_J1),
+ S5PC1XX_GPIO_J3_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_J2),
+ S5PC1XX_GPIO_J4_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_J3),
+ S5PC1XX_GPIO_K0_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_J4),
+ S5PC1XX_GPIO_K1_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_K0),
+ S5PC1XX_GPIO_K2_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_K1),
+ S5PC1XX_GPIO_K3_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_K2),
+ S5PC1XX_GPIO_MP00_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_K3),
+ S5PC1XX_GPIO_MP01_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_MP00),
+ S5PC1XX_GPIO_MP02_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_MP01),
+ S5PC1XX_GPIO_MP03_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_MP02),
+ S5PC1XX_GPIO_MP04_START = S5PC1XX_GPIO_NEXT(S5PC1XX_GPIO_MP03),
+};
+
+/* S5PC1XX GPIO number definitions. */
+#define S5PC1XX_GPA0(_nr) (S5PC1XX_GPIO_A0_START + (_nr))
+#define S5PC1XX_GPA1(_nr) (S5PC1XX_GPIO_A1_START + (_nr))
+#define S5PC1XX_GPB(_nr) (S5PC1XX_GPIO_B_START + (_nr))
+#define S5PC1XX_GPC(_nr) (S5PC1XX_GPIO_C_START + (_nr))
+#define S5PC1XX_GPD(_nr) (S5PC1XX_GPIO_D_START + (_nr))
+#define S5PC1XX_GPE0(_nr) (S5PC1XX_GPIO_E0_START + (_nr))
+#define S5PC1XX_GPE1(_nr) (S5PC1XX_GPIO_E1_START + (_nr))
+#define S5PC1XX_GPF0(_nr) (S5PC1XX_GPIO_F0_START + (_nr))
+#define S5PC1XX_GPF1(_nr) (S5PC1XX_GPIO_F1_START + (_nr))
+#define S5PC1XX_GPF2(_nr) (S5PC1XX_GPIO_F2_START + (_nr))
+#define S5PC1XX_GPF3(_nr) (S5PC1XX_GPIO_F3_START + (_nr))
+#define S5PC1XX_GPG0(_nr) (S5PC1XX_GPIO_G0_START + (_nr))
+#define S5PC1XX_GPG1(_nr) (S5PC1XX_GPIO_G1_START + (_nr))
+#define S5PC1XX_GPG2(_nr) (S5PC1XX_GPIO_G2_START + (_nr))
+#define S5PC1XX_GPG3(_nr) (S5PC1XX_GPIO_G3_START + (_nr))
+#define S5PC1XX_GPH0(_nr) (S5PC1XX_GPIO_H0_START + (_nr))
+#define S5PC1XX_GPH1(_nr) (S5PC1XX_GPIO_H1_START + (_nr))
+#define S5PC1XX_GPH2(_nr) (S5PC1XX_GPIO_H2_START + (_nr))
+#define S5PC1XX_GPH3(_nr) (S5PC1XX_GPIO_H3_START + (_nr))
+#define S5PC1XX_GPI(_nr) (S5PC1XX_GPIO_I_START + (_nr))
+#define S5PC1XX_GPJ0(_nr) (S5PC1XX_GPIO_J0_START + (_nr))
+#define S5PC1XX_GPJ1(_nr) (S5PC1XX_GPIO_J1_START + (_nr))
+#define S5PC1XX_GPJ2(_nr) (S5PC1XX_GPIO_J2_START + (_nr))
+#define S5PC1XX_GPJ3(_nr) (S5PC1XX_GPIO_J3_START + (_nr))
+#define S5PC1XX_GPJ4(_nr) (S5PC1XX_GPIO_J4_START + (_nr))
+#define S5PC1XX_GPK0(_nr) (S5PC1XX_GPIO_K0_START + (_nr))
+#define S5PC1XX_GPK1(_nr) (S5PC1XX_GPIO_K1_START + (_nr))
+#define S5PC1XX_GPK2(_nr) (S5PC1XX_GPIO_K2_START + (_nr))
+#define S5PC1XX_GPK3(_nr) (S5PC1XX_GPIO_K3_START + (_nr))
+#define S5PC1XX_MP00(_nr) (S5PC1XX_GPIO_MP00_START + (_nr))
+#define S5PC1XX_MP01(_nr) (S5PC1XX_GPIO_MP01_START + (_nr))
+#define S5PC1XX_MP02(_nr) (S5PC1XX_GPIO_MP02_START + (_nr))
+#define S5PC1XX_MP03(_nr) (S5PC1XX_GPIO_MP03_START + (_nr))
+#define S5PC1XX_MP04(_nr) (S5PC1XX_GPIO_MP04_START + (_nr))
+
+/* the end of the S5PC1XX specific gpios */
+#define S5PC1XX_GPIO_END (S5PC1XX_MP04(S5PC1XX_GPIO_MP04_NR) + 1)
+#define S3C_GPIO_END S5PC1XX_GPIO_END
+
+/* define the number of gpios we need to the one after the MP04() range */
+#define ARCH_NR_GPIOS (S5PC1XX_MP04(S5PC1XX_GPIO_MP04_NR) + 1)
+
+#include <asm-generic/gpio.h>
diff --git a/arch/arm/mach-s5pc100/include/mach/hardware.h b/arch/arm/mach-s5pc100/include/mach/hardware.h
new file mode 100644
index 00000000000..6b38618c2fd
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/hardware.h
@@ -0,0 +1,14 @@
+/* linux/arch/arm/mach-s5pc100/include/mach/hardware.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC100 - Hardware support
+ */
+
+#ifndef __ASM_ARCH_HARDWARE_H
+#define __ASM_ARCH_HARDWARE_H __FILE__
+
+/* currently nothing here, placeholder */
+
+#endif /* __ASM_ARCH_HARDWARE_H */
diff --git a/arch/arm/mach-s5pc100/include/mach/irqs.h b/arch/arm/mach-s5pc100/include/mach/irqs.h
new file mode 100644
index 00000000000..622720dba28
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/irqs.h
@@ -0,0 +1,14 @@
+/* linux/arch/arm/mach-s5pc100/include/mach/irqs.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC100 - IRQ definitions
+ */
+
+#ifndef __ASM_ARCH_IRQS_H
+#define __ASM_ARCH_IRQS_H __FILE__
+
+#include <plat/irqs.h>
+
+#endif /* __ASM_ARCH_IRQ_H */
diff --git a/arch/arm/mach-s5pc100/include/mach/map.h b/arch/arm/mach-s5pc100/include/mach/map.h
new file mode 100644
index 00000000000..9e9f39130b2
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/map.h
@@ -0,0 +1,75 @@
+/* linux/arch/arm/mach-s5pc100/include/mach/map.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * Based on mach-s3c6400/include/mach/map.h
+ *
+ * S5PC1XX - Memory map definitions
+ *
+ * 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_ARCH_MAP_H
+#define __ASM_ARCH_MAP_H __FILE__
+
+#include <plat/map-base.h>
+
+
+/* Chip ID */
+#define S5PC100_PA_CHIPID (0xE0000000)
+#define S5PC1XX_PA_CHIPID S5PC100_PA_CHIPID
+#define S5PC1XX_VA_CHIPID S3C_VA_SYS
+
+/* System */
+#define S5PC100_PA_SYS (0xE0100000)
+#define S5PC100_PA_CLK (S5PC100_PA_SYS + 0x0)
+#define S5PC100_PA_PWR (S5PC100_PA_SYS + 0x8000)
+#define S5PC1XX_PA_CLK S5PC100_PA_CLK
+#define S5PC1XX_PA_PWR S5PC100_PA_PWR
+#define S5PC1XX_VA_CLK (S3C_VA_SYS + 0x10000)
+#define S5PC1XX_VA_PWR (S3C_VA_SYS + 0x20000)
+
+/* Interrupt */
+#define S5PC100_PA_VIC (0xE4000000)
+#define S5PC100_VA_VIC S3C_VA_IRQ
+#define S5PC100_PA_VIC_OFFSET 0x100000
+#define S5PC100_VA_VIC_OFFSET 0x10000
+#define S5PC1XX_PA_VIC(x) (S5PC100_PA_VIC + ((x) * S5PC100_PA_VIC_OFFSET))
+#define S5PC1XX_VA_VIC(x) (S5PC100_VA_VIC + ((x) * S5PC100_VA_VIC_OFFSET))
+
+/* Timer */
+#define S5PC100_PA_TIMER (0xEA000000)
+#define S5PC1XX_PA_TIMER S5PC100_PA_TIMER
+#define S5PC1XX_VA_TIMER S3C_VA_TIMER
+
+/* UART */
+#define S5PC100_PA_UART (0xEC000000)
+#define S5PC1XX_PA_UART S5PC100_PA_UART
+#define S5PC1XX_VA_UART S3C_VA_UART
+
+/* IIC */
+#define S5PC100_PA_IIC (0xEC100000)
+
+/* ETC */
+#define S5PC100_PA_SDRAM (0x20000000)
+
+/* compatibility defines. */
+#define S3C_PA_UART S5PC100_PA_UART
+#define S3C_PA_UART0 (S5PC100_PA_UART + 0x0)
+#define S3C_PA_UART1 (S5PC100_PA_UART + 0x400)
+#define S3C_PA_UART2 (S5PC100_PA_UART + 0x800)
+#define S3C_PA_UART3 (S5PC100_PA_UART + 0xC00)
+#define S3C_VA_UART0 (S3C_VA_UART + 0x0)
+#define S3C_VA_UART1 (S3C_VA_UART + 0x400)
+#define S3C_VA_UART2 (S3C_VA_UART + 0x800)
+#define S3C_VA_UART3 (S3C_VA_UART + 0xC00)
+#define S3C_UART_OFFSET 0x400
+#define S3C_VA_VIC0 (S3C_VA_IRQ + 0x0)
+#define S3C_VA_VIC1 (S3C_VA_IRQ + 0x10000)
+#define S3C_VA_VIC2 (S3C_VA_IRQ + 0x20000)
+#define S3C_PA_IIC S5PC100_PA_IIC
+
+#endif /* __ASM_ARCH_C100_MAP_H */
diff --git a/arch/arm/mach-s5pc100/include/mach/memory.h b/arch/arm/mach-s5pc100/include/mach/memory.h
new file mode 100644
index 00000000000..4b60d18179f
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/memory.h
@@ -0,0 +1,18 @@
+/* arch/arm/mach-s5pc100/include/mach/memory.h
+ *
+ * Copyright 2008 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * Based on mach-s3c6400/include/mach/memory.h
+ *
+ * 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_ARCH_MEMORY_H
+#define __ASM_ARCH_MEMORY_H
+
+#define PHYS_OFFSET UL(0x20000000)
+
+#endif
diff --git a/arch/arm/mach-s5pc100/include/mach/pwm-clock.h b/arch/arm/mach-s5pc100/include/mach/pwm-clock.h
new file mode 100644
index 00000000000..b34d2f7aae5
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/pwm-clock.h
@@ -0,0 +1,56 @@
+/* linux/arch/arm/mach-s5pc100/include/mach/pwm-clock.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC100 - pwm clock and timer support
+ *
+ * Based on mach-s3c6400/include/mach/pwm-clock.h
+ */
+
+/**
+ * pwm_cfg_src_is_tclk() - return whether the given mux config is a tclk
+ * @tcfg: The timer TCFG1 register bits shifted down to 0.
+ *
+ * Return true if the given configuration from TCFG1 is a TCLK instead
+ * any of the TDIV clocks.
+ */
+static inline int pwm_cfg_src_is_tclk(unsigned long tcfg)
+{
+ return tcfg >= S3C64XX_TCFG1_MUX_TCLK;
+}
+
+/**
+ * tcfg_to_divisor() - convert tcfg1 setting to a divisor
+ * @tcfg1: The tcfg1 setting, shifted down.
+ *
+ * Get the divisor value for the given tcfg1 setting. We assume the
+ * caller has already checked to see if this is not a TCLK source.
+ */
+static inline unsigned long tcfg_to_divisor(unsigned long tcfg1)
+{
+ return 1 << tcfg1;
+}
+
+/**
+ * pwm_tdiv_has_div1() - does the tdiv setting have a /1
+ *
+ * Return true if we have a /1 in the tdiv setting.
+ */
+static inline unsigned int pwm_tdiv_has_div1(void)
+{
+ return 1;
+}
+
+/**
+ * pwm_tdiv_div_bits() - calculate TCFG1 divisor value.
+ * @div: The divisor to calculate the bit information for.
+ *
+ * Turn a divisor into the necessary bit field for TCFG1.
+ */
+static inline unsigned long pwm_tdiv_div_bits(unsigned int div)
+{
+ return ilog2(div);
+}
+
+#define S3C_TCFG1_MUX_TCLK S3C64XX_TCFG1_MUX_TCLK
diff --git a/arch/arm/mach-s5pc100/include/mach/regs-irq.h b/arch/arm/mach-s5pc100/include/mach/regs-irq.h
new file mode 100644
index 00000000000..751ac15438c
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/regs-irq.h
@@ -0,0 +1,24 @@
+/* linux/arch/arm/mach-s5pc100/include/mach/regs-irq.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC1XX - IRQ register definitions
+ *
+ * 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_ARCH_REGS_IRQ_H
+#define __ASM_ARCH_REGS_IRQ_H __FILE__
+
+#include <mach/map.h>
+#include <asm/hardware/vic.h>
+
+/* interrupt controller */
+#define S5PC1XX_VIC0REG(x) ((x) + S5PC1XX_VA_VIC(0))
+#define S5PC1XX_VIC1REG(x) ((x) + S5PC1XX_VA_VIC(1))
+#define S5PC1XX_VIC2REG(x) ((x) + S5PC1XX_VA_VIC(2))
+
+#endif /* __ASM_ARCH_REGS_IRQ_H */
diff --git a/arch/arm/mach-s5pc100/include/mach/system.h b/arch/arm/mach-s5pc100/include/mach/system.h
new file mode 100644
index 00000000000..e3901437547
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/system.h
@@ -0,0 +1,24 @@
+/* linux/arch/arm/mach-s5pc100/include/mach/system.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC1XX - system implementation
+ *
+ * Based on mach-s3c6400/include/mach/system.h
+ */
+
+#ifndef __ASM_ARCH_SYSTEM_H
+#define __ASM_ARCH_SYSTEM_H __FILE__
+
+static void arch_idle(void)
+{
+ /* nothing here yet */
+}
+
+static void arch_reset(char mode, const char *cmd)
+{
+ /* nothing here yet */
+}
+
+#endif /* __ASM_ARCH_IRQ_H */
diff --git a/arch/arm/mach-s5pc100/include/mach/tick.h b/arch/arm/mach-s5pc100/include/mach/tick.h
new file mode 100644
index 00000000000..d3de0f3591a
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/tick.h
@@ -0,0 +1,29 @@
+/* linux/arch/arm/mach-s5pc100/include/mach/tick.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S3C64XX - Timer tick support definitions
+ *
+ * Based on mach-s3c6400/include/mach/tick.h
+ *
+ * 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_ARCH_TICK_H
+#define __ASM_ARCH_TICK_H __FILE__
+
+/* note, the timer interrutps turn up in 2 places, the vic and then
+ * the timer block. We take the VIC as the base at the moment.
+ */
+static inline u32 s3c24xx_ostimer_pending(void)
+{
+ u32 pend = __raw_readl(S3C_VA_VIC0 + VIC_RAW_STATUS);
+ return pend & 1 << (IRQ_TIMER4 - S5PC1XX_IRQ_VIC0(0));
+}
+
+#define TICK_MAX (0xffffffff)
+
+#endif /* __ASM_ARCH_TICK_H */
diff --git a/arch/arm/mach-s5pc100/include/mach/uncompress.h b/arch/arm/mach-s5pc100/include/mach/uncompress.h
new file mode 100644
index 00000000000..01ccf535e76
--- /dev/null
+++ b/arch/arm/mach-s5pc100/include/mach/uncompress.h
@@ -0,0 +1,28 @@
+/* arch/arm/mach-s5pc100/include/mach/uncompress.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC100 - uncompress code
+ *
+ * Based on mach-s3c6400/include/mach/uncompress.h
+ *
+ * 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_ARCH_UNCOMPRESS_H
+#define __ASM_ARCH_UNCOMPRESS_H
+
+#include <mach/map.h>
+#include <plat/uncompress.h>
+
+static void arch_detect_cpu(void)
+{
+ /* we do not need to do any cpu detection here at the moment. */
+ fifo_mask = S3C2440_UFSTAT_TXMASK;
+ fifo_max = 63 << S3C2440_UFSTAT_TXSHIFT;
+}
+
+#endif /* __ASM_ARCH_UNCOMPRESS_H */
diff --git a/arch/arm/mach-s5pc100/mach-smdkc100.c b/arch/arm/mach-s5pc100/mach-smdkc100.c
new file mode 100644
index 00000000000..214093cd763
--- /dev/null
+++ b/arch/arm/mach-s5pc100/mach-smdkc100.c
@@ -0,0 +1,103 @@
+/* linux/arch/arm/mach-s5pc100/mach-smdkc100.c
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Author: Byungho Min <bhmin@samsung.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/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+#include <linux/delay.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/map.h>
+
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include <plat/regs-serial.h>
+
+#include <plat/clock.h>
+#include <plat/devs.h>
+#include <plat/cpu.h>
+#include <plat/s5pc100.h>
+
+#define UCON (S3C2410_UCON_DEFAULT | S3C2410_UCON_UCLK)
+#define ULCON (S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB)
+#define UFCON (S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE)
+
+static struct s3c2410_uartcfg smdkc100_uartcfgs[] __initdata = {
+ [0] = {
+ .hwport = 0,
+ .flags = 0,
+ .ucon = 0x3c5,
+ .ulcon = 0x03,
+ .ufcon = 0x51,
+ },
+ [1] = {
+ .hwport = 1,
+ .flags = 0,
+ .ucon = 0x3c5,
+ .ulcon = 0x03,
+ .ufcon = 0x51,
+ },
+ [2] = {
+ .hwport = 2,
+ .flags = 0,
+ .ucon = 0x3c5,
+ .ulcon = 0x03,
+ .ufcon = 0x51,
+ },
+ [3] = {
+ .hwport = 3,
+ .flags = 0,
+ .ucon = 0x3c5,
+ .ulcon = 0x03,
+ .ufcon = 0x51,
+ },
+};
+
+static struct map_desc smdkc100_iodesc[] = {};
+
+static struct platform_device *smdkc100_devices[] __initdata = {
+};
+
+static void __init smdkc100_map_io(void)
+{
+ s5pc1xx_init_io(smdkc100_iodesc, ARRAY_SIZE(smdkc100_iodesc));
+ s3c24xx_init_clocks(12000000);
+ s3c24xx_init_uarts(smdkc100_uartcfgs, ARRAY_SIZE(smdkc100_uartcfgs));
+}
+
+static void __init smdkc100_machine_init(void)
+{
+ platform_add_devices(smdkc100_devices, ARRAY_SIZE(smdkc100_devices));
+}
+
+MACHINE_START(SMDKC100, "SMDKC100")
+ /* Maintainer: Byungho Min <bhmin@samsung.com> */
+ .phys_io = S5PC1XX_PA_UART & 0xfff00000,
+ .io_pg_offst = (((u32)S5PC1XX_VA_UART) >> 18) & 0xfffc,
+ .boot_params = S5PC100_PA_SDRAM + 0x100,
+
+ .init_irq = s5pc100_init_irq,
+ .map_io = smdkc100_map_io,
+ .init_machine = smdkc100_machine_init,
+ .timer = &s3c24xx_timer,
+MACHINE_END
diff --git a/arch/arm/mach-u300/mmc.c b/arch/arm/mach-u300/mmc.c
index 3138d3955c9..585cc013639 100644
--- a/arch/arm/mach-u300/mmc.c
+++ b/arch/arm/mach-u300/mmc.c
@@ -156,6 +156,8 @@ int __devinit mmc_init(struct amba_device *adev)
mmci_card->mmc0_plat_data.ocr_mask = MMC_VDD_28_29;
mmci_card->mmc0_plat_data.translate_vdd = mmc_translate_vdd;
mmci_card->mmc0_plat_data.status = mmc_status;
+ mmci_card->mmc0_plat_data.gpio_wp = -1;
+ mmci_card->mmc0_plat_data.gpio_cd = -1;
mmcsd_device->platform_data = (void *) &mmci_card->mmc0_plat_data;
diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c
index 31093af7d05..975eae41ee6 100644
--- a/arch/arm/mach-versatile/core.c
+++ b/arch/arm/mach-versatile/core.c
@@ -26,6 +26,7 @@
#include <linux/interrupt.h>
#include <linux/amba/bus.h>
#include <linux/amba/clcd.h>
+#include <linux/amba/pl061.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/cnt32_to_63.h>
@@ -371,6 +372,8 @@ unsigned int mmc_status(struct device *dev)
static struct mmc_platform_data mmc0_plat_data = {
.ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
.status = mmc_status,
+ .gpio_wp = -1,
+ .gpio_cd = -1,
};
/*
@@ -705,6 +708,16 @@ static struct clcd_board clcd_plat_data = {
.remove = versatile_clcd_remove,
};
+static struct pl061_platform_data gpio0_plat_data = {
+ .gpio_base = 0,
+ .irq_base = IRQ_GPIO0_START,
+};
+
+static struct pl061_platform_data gpio1_plat_data = {
+ .gpio_base = 8,
+ .irq_base = IRQ_GPIO1_START,
+};
+
#define AACI_IRQ { IRQ_AACI, NO_IRQ }
#define AACI_DMA { 0x80, 0x81 }
#define MMCI0_IRQ { IRQ_MMCI0A,IRQ_SIC_MMCI0B }
@@ -767,8 +780,8 @@ AMBA_DEVICE(clcd, "dev:20", CLCD, &clcd_plat_data);
AMBA_DEVICE(dmac, "dev:30", DMAC, NULL);
AMBA_DEVICE(sctl, "dev:e0", SCTL, NULL);
AMBA_DEVICE(wdog, "dev:e1", WATCHDOG, NULL);
-AMBA_DEVICE(gpio0, "dev:e4", GPIO0, NULL);
-AMBA_DEVICE(gpio1, "dev:e5", GPIO1, NULL);
+AMBA_DEVICE(gpio0, "dev:e4", GPIO0, &gpio0_plat_data);
+AMBA_DEVICE(gpio1, "dev:e5", GPIO1, &gpio1_plat_data);
AMBA_DEVICE(rtc, "dev:e8", RTC, NULL);
AMBA_DEVICE(sci0, "dev:f0", SCI, NULL);
AMBA_DEVICE(uart0, "dev:f1", UART0, NULL);
diff --git a/arch/arm/mach-versatile/include/mach/gpio.h b/arch/arm/mach-versatile/include/mach/gpio.h
new file mode 100644
index 00000000000..94ff27678a4
--- /dev/null
+++ b/arch/arm/mach-versatile/include/mach/gpio.h
@@ -0,0 +1,6 @@
+#include <asm-generic/gpio.h>
+
+#define gpio_get_value __gpio_get_value
+#define gpio_set_value __gpio_set_value
+#define gpio_cansleep __gpio_cansleep
+#define gpio_to_irq __gpio_to_irq
diff --git a/arch/arm/mach-versatile/include/mach/irqs.h b/arch/arm/mach-versatile/include/mach/irqs.h
index 9bfdb30e1f3..bf44c61bd1f 100644
--- a/arch/arm/mach-versatile/include/mach/irqs.h
+++ b/arch/arm/mach-versatile/include/mach/irqs.h
@@ -122,4 +122,13 @@
#define IRQ_SIC_PCI3 (IRQ_SIC_START + SIC_INT_PCI3)
#define IRQ_SIC_END 63
-#define NR_IRQS 64
+#define IRQ_GPIO0_START (IRQ_SIC_END + 1)
+#define IRQ_GPIO0_END (IRQ_GPIO0_START + 31)
+#define IRQ_GPIO1_START (IRQ_GPIO0_END + 1)
+#define IRQ_GPIO1_END (IRQ_GPIO1_START + 31)
+#define IRQ_GPIO2_START (IRQ_GPIO1_END + 1)
+#define IRQ_GPIO2_END (IRQ_GPIO2_START + 31)
+#define IRQ_GPIO3_START (IRQ_GPIO2_END + 1)
+#define IRQ_GPIO3_END (IRQ_GPIO3_START + 31)
+
+#define NR_IRQS (IRQ_GPIO3_END + 1)
diff --git a/arch/arm/mach-versatile/versatile_pb.c b/arch/arm/mach-versatile/versatile_pb.c
index aa051c0884f..9af8d8154df 100644
--- a/arch/arm/mach-versatile/versatile_pb.c
+++ b/arch/arm/mach-versatile/versatile_pb.c
@@ -23,6 +23,7 @@
#include <linux/device.h>
#include <linux/sysdev.h>
#include <linux/amba/bus.h>
+#include <linux/amba/pl061.h>
#include <linux/io.h>
#include <mach/hardware.h>
@@ -43,6 +44,18 @@
static struct mmc_platform_data mmc1_plat_data = {
.ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
.status = mmc_status,
+ .gpio_wp = -1,
+ .gpio_cd = -1,
+};
+
+static struct pl061_platform_data gpio2_plat_data = {
+ .gpio_base = 16,
+ .irq_base = IRQ_GPIO2_START,
+};
+
+static struct pl061_platform_data gpio3_plat_data = {
+ .gpio_base = 24,
+ .irq_base = IRQ_GPIO3_START,
};
#define UART3_IRQ { IRQ_SIC_UART3, NO_IRQ }
@@ -70,8 +83,8 @@ AMBA_DEVICE(sci1, "fpga:0a", SCI1, NULL);
AMBA_DEVICE(mmc1, "fpga:0b", MMCI1, &mmc1_plat_data);
/* DevChip Primecells */
-AMBA_DEVICE(gpio2, "dev:e6", GPIO2, NULL);
-AMBA_DEVICE(gpio3, "dev:e7", GPIO3, NULL);
+AMBA_DEVICE(gpio2, "dev:e6", GPIO2, &gpio2_plat_data);
+AMBA_DEVICE(gpio3, "dev:e7", GPIO3, &gpio3_plat_data);
static struct amba_device *amba_devs[] __initdata = {
&uart3_device,
diff --git a/arch/arm/mach-w90x900/Kconfig b/arch/arm/mach-w90x900/Kconfig
index 8e4178fe5ec..69bab32a8bc 100644
--- a/arch/arm/mach-w90x900/Kconfig
+++ b/arch/arm/mach-w90x900/Kconfig
@@ -5,6 +5,16 @@ config CPU_W90P910
help
Support for W90P910 of Nuvoton W90X900 CPUs.
+config CPU_NUC950
+ bool
+ help
+ Support for NUCP950 of Nuvoton NUC900 CPUs.
+
+config CPU_NUC960
+ bool
+ help
+ Support for NUCP960 of Nuvoton NUC900 CPUs.
+
menu "W90P910 Machines"
config MACH_W90P910EVB
@@ -16,4 +26,24 @@ config MACH_W90P910EVB
endmenu
+menu "NUC950 Machines"
+
+config MACH_W90P950EVB
+ bool "Nuvoton NUC950 Evaluation Board"
+ select CPU_NUC950
+ help
+ Say Y here if you are using the Nuvoton NUC950EVB
+
+endmenu
+
+menu "NUC960 Machines"
+
+config MACH_W90N960EVB
+ bool "Nuvoton NUC960 Evaluation Board"
+ select CPU_NUC960
+ help
+ Say Y here if you are using the Nuvoton NUC960EVB
+
+endmenu
+
endif
diff --git a/arch/arm/mach-w90x900/Makefile b/arch/arm/mach-w90x900/Makefile
index d50c94f4dbd..828c0326441 100644
--- a/arch/arm/mach-w90x900/Makefile
+++ b/arch/arm/mach-w90x900/Makefile
@@ -4,12 +4,16 @@
# Object file lists.
-obj-y := irq.o time.o mfp-w90p910.o gpio.o clock.o
-
+obj-y := irq.o time.o mfp.o gpio.o clock.o
+obj-y += clksel.o dev.o cpu.o
# W90X900 CPU support files
-obj-$(CONFIG_CPU_W90P910) += w90p910.o
+obj-$(CONFIG_CPU_W90P910) += nuc910.o
+obj-$(CONFIG_CPU_NUC950) += nuc950.o
+obj-$(CONFIG_CPU_NUC960) += nuc960.o
# machine support
-obj-$(CONFIG_MACH_W90P910EVB) += mach-w90p910evb.o
+obj-$(CONFIG_MACH_W90P910EVB) += mach-nuc910evb.o
+obj-$(CONFIG_MACH_W90P950EVB) += mach-nuc950evb.o
+obj-$(CONFIG_MACH_W90N960EVB) += mach-nuc960evb.o
diff --git a/arch/arm/mach-w90x900/clksel.c b/arch/arm/mach-w90x900/clksel.c
new file mode 100644
index 00000000000..3de4a5211c3
--- /dev/null
+++ b/arch/arm/mach-w90x900/clksel.c
@@ -0,0 +1,91 @@
+/*
+ * linux/arch/arm/mach-w90x900/clksel.c
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-clock.h>
+
+#define PLL0 0x00
+#define PLL1 0x01
+#define OTHER 0x02
+#define EXT 0x03
+#define MSOFFSET 0x0C
+#define ATAOFFSET 0x0a
+#define LCDOFFSET 0x06
+#define AUDOFFSET 0x04
+#define CPUOFFSET 0x00
+
+static DEFINE_MUTEX(clksel_sem);
+
+static void clock_source_select(const char *dev_id, unsigned int clkval)
+{
+ unsigned int clksel, offset;
+
+ clksel = __raw_readl(REG_CLKSEL);
+
+ if (strcmp(dev_id, "nuc900-ms") == 0)
+ offset = MSOFFSET;
+ else if (strcmp(dev_id, "nuc900-atapi") == 0)
+ offset = ATAOFFSET;
+ else if (strcmp(dev_id, "nuc900-lcd") == 0)
+ offset = LCDOFFSET;
+ else if (strcmp(dev_id, "nuc900-audio") == 0)
+ offset = AUDOFFSET;
+ else
+ offset = CPUOFFSET;
+
+ clksel &= ~(0x03 << offset);
+ clksel |= (clkval << offset);
+
+ __raw_writel(clksel, REG_CLKSEL);
+}
+
+void nuc900_clock_source(struct device *dev, unsigned char *src)
+{
+ unsigned int clkval;
+ const char *dev_id;
+
+ BUG_ON(!src);
+ clkval = 0;
+
+ mutex_lock(&clksel_sem);
+
+ if (dev)
+ dev_id = dev_name(dev);
+ else
+ dev_id = "cpufreq";
+
+ if (strcmp(src, "pll0") == 0)
+ clkval = PLL0;
+ else if (strcmp(src, "pll1") == 0)
+ clkval = PLL1;
+ else if (strcmp(src, "ext") == 0)
+ clkval = EXT;
+ else if (strcmp(src, "oth") == 0)
+ clkval = OTHER;
+
+ clock_source_select(dev_id, clkval);
+
+ mutex_unlock(&clksel_sem);
+}
+EXPORT_SYMBOL(nuc900_clock_source);
+
diff --git a/arch/arm/mach-w90x900/clock.c b/arch/arm/mach-w90x900/clock.c
index f420613cd39..b785994bab0 100644
--- a/arch/arm/mach-w90x900/clock.c
+++ b/arch/arm/mach-w90x900/clock.c
@@ -25,6 +25,8 @@
#include "clock.h"
+#define SUBCLK 0x24
+
static DEFINE_SPINLOCK(clocks_lock);
int clk_enable(struct clk *clk)
@@ -53,7 +55,13 @@ void clk_disable(struct clk *clk)
}
EXPORT_SYMBOL(clk_disable);
-void w90x900_clk_enable(struct clk *clk, int enable)
+unsigned long clk_get_rate(struct clk *clk)
+{
+ return 15000000;
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+void nuc900_clk_enable(struct clk *clk, int enable)
{
unsigned int clocks = clk->cken;
unsigned long clken;
@@ -68,6 +76,22 @@ void w90x900_clk_enable(struct clk *clk, int enable)
__raw_writel(clken, W90X900_VA_CLKPWR);
}
+void nuc900_subclk_enable(struct clk *clk, int enable)
+{
+ unsigned int clocks = clk->cken;
+ unsigned long clken;
+
+ clken = __raw_readl(W90X900_VA_CLKPWR + SUBCLK);
+
+ if (enable)
+ clken |= clocks;
+ else
+ clken &= ~clocks;
+
+ __raw_writel(clken, W90X900_VA_CLKPWR + SUBCLK);
+}
+
+
void clks_register(struct clk_lookup *clks, size_t num)
{
int i;
diff --git a/arch/arm/mach-w90x900/clock.h b/arch/arm/mach-w90x900/clock.h
index 4f27bda76d5..f5816a06eed 100644
--- a/arch/arm/mach-w90x900/clock.h
+++ b/arch/arm/mach-w90x900/clock.h
@@ -12,7 +12,8 @@
#include <asm/clkdev.h>
-void w90x900_clk_enable(struct clk *clk, int enable);
+void nuc900_clk_enable(struct clk *clk, int enable);
+void nuc900_subclk_enable(struct clk *clk, int enable);
void clks_register(struct clk_lookup *clks, size_t num);
struct clk {
@@ -23,10 +24,17 @@ struct clk {
#define DEFINE_CLK(_name, _ctrlbit) \
struct clk clk_##_name = { \
- .enable = w90x900_clk_enable, \
+ .enable = nuc900_clk_enable, \
.cken = (1 << _ctrlbit), \
}
+#define DEFINE_SUBCLK(_name, _ctrlbit) \
+struct clk clk_##_name = { \
+ .enable = nuc900_subclk_enable, \
+ .cken = (1 << _ctrlbit), \
+ }
+
+
#define DEF_CLKLOOK(_clk, _devname, _conname) \
{ \
.clk = _clk, \
diff --git a/arch/arm/mach-w90x900/cpu.c b/arch/arm/mach-w90x900/cpu.c
new file mode 100644
index 00000000000..921cef991bf
--- /dev/null
+++ b/arch/arm/mach-w90x900/cpu.c
@@ -0,0 +1,212 @@
+/*
+ * linux/arch/arm/mach-w90x900/cpu.c
+ *
+ * Copyright (c) 2009 Nuvoton corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * NUC900 series cpu common support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/serial_8250.h>
+#include <linux/delay.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+#include <asm/irq.h>
+
+#include <mach/hardware.h>
+#include <mach/regs-serial.h>
+#include <mach/regs-clock.h>
+#include <mach/regs-ebi.h>
+
+#include "cpu.h"
+#include "clock.h"
+
+/* Initial IO mappings */
+
+static struct map_desc nuc900_iodesc[] __initdata = {
+ IODESC_ENT(IRQ),
+ IODESC_ENT(GCR),
+ IODESC_ENT(UART),
+ IODESC_ENT(TIMER),
+ IODESC_ENT(EBI),
+};
+
+/* Initial clock declarations. */
+static DEFINE_CLK(lcd, 0);
+static DEFINE_CLK(audio, 1);
+static DEFINE_CLK(fmi, 4);
+static DEFINE_SUBCLK(ms, 0);
+static DEFINE_SUBCLK(sd, 1);
+static DEFINE_CLK(dmac, 5);
+static DEFINE_CLK(atapi, 6);
+static DEFINE_CLK(emc, 7);
+static DEFINE_SUBCLK(rmii, 2);
+static DEFINE_CLK(usbd, 8);
+static DEFINE_CLK(usbh, 9);
+static DEFINE_CLK(g2d, 10);;
+static DEFINE_CLK(pwm, 18);
+static DEFINE_CLK(ps2, 24);
+static DEFINE_CLK(kpi, 25);
+static DEFINE_CLK(wdt, 26);
+static DEFINE_CLK(gdma, 27);
+static DEFINE_CLK(adc, 28);
+static DEFINE_CLK(usi, 29);
+static DEFINE_CLK(ext, 0);
+
+static struct clk_lookup nuc900_clkregs[] = {
+ DEF_CLKLOOK(&clk_lcd, "nuc900-lcd", NULL),
+ DEF_CLKLOOK(&clk_audio, "nuc900-audio", NULL),
+ DEF_CLKLOOK(&clk_fmi, "nuc900-fmi", NULL),
+ DEF_CLKLOOK(&clk_ms, "nuc900-fmi", "MS"),
+ DEF_CLKLOOK(&clk_sd, "nuc900-fmi", "SD"),
+ DEF_CLKLOOK(&clk_dmac, "nuc900-dmac", NULL),
+ DEF_CLKLOOK(&clk_atapi, "nuc900-atapi", NULL),
+ DEF_CLKLOOK(&clk_emc, "nuc900-emc", NULL),
+ DEF_CLKLOOK(&clk_rmii, "nuc900-emc", "RMII"),
+ DEF_CLKLOOK(&clk_usbd, "nuc900-usbd", NULL),
+ DEF_CLKLOOK(&clk_usbh, "nuc900-usbh", NULL),
+ DEF_CLKLOOK(&clk_g2d, "nuc900-g2d", NULL),
+ DEF_CLKLOOK(&clk_pwm, "nuc900-pwm", NULL),
+ DEF_CLKLOOK(&clk_ps2, "nuc900-ps2", NULL),
+ DEF_CLKLOOK(&clk_kpi, "nuc900-kpi", NULL),
+ DEF_CLKLOOK(&clk_wdt, "nuc900-wdt", NULL),
+ DEF_CLKLOOK(&clk_gdma, "nuc900-gdma", NULL),
+ DEF_CLKLOOK(&clk_adc, "nuc900-adc", NULL),
+ DEF_CLKLOOK(&clk_usi, "nuc900-spi", NULL),
+ DEF_CLKLOOK(&clk_ext, NULL, "ext"),
+};
+
+/* Initial serial platform data */
+
+struct plat_serial8250_port nuc900_uart_data[] = {
+ NUC900_8250PORT(UART0),
+};
+
+struct platform_device nuc900_serial_device = {
+ .name = "serial8250",
+ .id = PLAT8250_DEV_PLATFORM,
+ .dev = {
+ .platform_data = nuc900_uart_data,
+ },
+};
+
+/*Set NUC900 series cpu frequence*/
+static int __init nuc900_set_clkval(unsigned int cpufreq)
+{
+ unsigned int pllclk, ahbclk, apbclk, val;
+
+ pllclk = 0;
+ ahbclk = 0;
+ apbclk = 0;
+
+ switch (cpufreq) {
+ case 66:
+ pllclk = PLL_66MHZ;
+ ahbclk = AHB_CPUCLK_1_1;
+ apbclk = APB_AHB_1_2;
+ break;
+
+ case 100:
+ pllclk = PLL_100MHZ;
+ ahbclk = AHB_CPUCLK_1_1;
+ apbclk = APB_AHB_1_2;
+ break;
+
+ case 120:
+ pllclk = PLL_120MHZ;
+ ahbclk = AHB_CPUCLK_1_2;
+ apbclk = APB_AHB_1_2;
+ break;
+
+ case 166:
+ pllclk = PLL_166MHZ;
+ ahbclk = AHB_CPUCLK_1_2;
+ apbclk = APB_AHB_1_2;
+ break;
+
+ case 200:
+ pllclk = PLL_200MHZ;
+ ahbclk = AHB_CPUCLK_1_2;
+ apbclk = APB_AHB_1_2;
+ break;
+ }
+
+ __raw_writel(pllclk, REG_PLLCON0);
+
+ val = __raw_readl(REG_CLKDIV);
+ val &= ~(0x03 << 24 | 0x03 << 26);
+ val |= (ahbclk << 24 | apbclk << 26);
+ __raw_writel(val, REG_CLKDIV);
+
+ return 0;
+}
+static int __init nuc900_set_cpufreq(char *str)
+{
+ unsigned long cpufreq, val;
+
+ if (!*str)
+ return 0;
+
+ strict_strtoul(str, 0, &cpufreq);
+
+ nuc900_clock_source(NULL, "ext");
+
+ nuc900_set_clkval(cpufreq);
+
+ mdelay(1);
+
+ val = __raw_readl(REG_CKSKEW);
+ val &= ~0xff;
+ val |= DEFAULTSKEW;
+ __raw_writel(val, REG_CKSKEW);
+
+ nuc900_clock_source(NULL, "pll0");
+
+ return 1;
+}
+
+__setup("cpufreq=", nuc900_set_cpufreq);
+
+/*Init NUC900 evb io*/
+
+void __init nuc900_map_io(struct map_desc *mach_desc, int mach_size)
+{
+ unsigned long idcode = 0x0;
+
+ iotable_init(mach_desc, mach_size);
+ iotable_init(nuc900_iodesc, ARRAY_SIZE(nuc900_iodesc));
+
+ idcode = __raw_readl(NUC900PDID);
+ if (idcode == NUC910_CPUID)
+ printk(KERN_INFO "CPU type 0x%08lx is NUC910\n", idcode);
+ else if (idcode == NUC920_CPUID)
+ printk(KERN_INFO "CPU type 0x%08lx is NUC920\n", idcode);
+ else if (idcode == NUC950_CPUID)
+ printk(KERN_INFO "CPU type 0x%08lx is NUC950\n", idcode);
+ else if (idcode == NUC960_CPUID)
+ printk(KERN_INFO "CPU type 0x%08lx is NUC960\n", idcode);
+}
+
+/*Init NUC900 clock*/
+
+void __init nuc900_init_clocks(void)
+{
+ clks_register(nuc900_clkregs, ARRAY_SIZE(nuc900_clkregs));
+}
+
diff --git a/arch/arm/mach-w90x900/cpu.h b/arch/arm/mach-w90x900/cpu.h
index 57b5dbabeb4..4d58ba164e2 100644
--- a/arch/arm/mach-w90x900/cpu.h
+++ b/arch/arm/mach-w90x900/cpu.h
@@ -6,7 +6,7 @@
* Copyright (c) 2008 Nuvoton technology corporation
* All rights reserved.
*
- * Header file for W90X900 CPU support
+ * Header file for NUC900 CPU support
*
* Wan ZongShun <mcuos.com@gmail.com>
*
@@ -24,29 +24,7 @@
.type = MT_DEVICE, \
}
-/*Cpu identifier register*/
-
-#define W90X900PDID W90X900_VA_GCR
-#define W90P910_CPUID 0x02900910
-#define W90P920_CPUID 0x02900920
-#define W90P950_CPUID 0x02900950
-#define W90N960_CPUID 0x02900960
-
-struct w90x900_uartcfg;
-struct map_desc;
-struct sys_timer;
-
-/* core initialisation functions */
-
-extern void w90x900_init_irq(void);
-extern void w90p910_init_io(struct map_desc *mach_desc, int size);
-extern void w90p910_init_uarts(struct w90x900_uartcfg *cfg, int no);
-extern void w90p910_init_clocks(void);
-extern void w90p910_map_io(struct map_desc *mach_desc, int size);
-extern struct platform_device w90p910_serial_device;
-extern struct sys_timer w90x900_timer;
-
-#define W90X900_8250PORT(name) \
+#define NUC900_8250PORT(name) \
{ \
.membase = name##_BA, \
.mapbase = name##_PA, \
@@ -56,3 +34,26 @@ extern struct sys_timer w90x900_timer;
.iotype = UPIO_MEM, \
.flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, \
}
+
+/*Cpu identifier register*/
+
+#define NUC900PDID W90X900_VA_GCR
+#define NUC910_CPUID 0x02900910
+#define NUC920_CPUID 0x02900920
+#define NUC950_CPUID 0x02900950
+#define NUC960_CPUID 0x02900960
+
+/* extern file from cpu.c */
+
+extern void nuc900_clock_source(struct device *dev, unsigned char *src);
+extern void nuc900_init_clocks(void);
+extern void nuc900_map_io(struct map_desc *mach_desc, int mach_size);
+extern void nuc900_board_init(struct platform_device **device, int size);
+
+/* for either public between 910 and 920, or between 920 and 950 */
+
+extern struct platform_device nuc900_serial_device;
+extern struct platform_device nuc900_device_fmi;
+extern struct platform_device nuc900_device_kpi;
+extern struct platform_device nuc900_device_rtc;
+extern struct platform_device nuc900_device_ts;
diff --git a/arch/arm/mach-w90x900/dev.c b/arch/arm/mach-w90x900/dev.c
new file mode 100644
index 00000000000..2a6f98de48d
--- /dev/null
+++ b/arch/arm/mach-w90x900/dev.c
@@ -0,0 +1,389 @@
+/*
+ * linux/arch/arm/mach-w90x900/dev.c
+ *
+ * Copyright (C) 2009 Nuvoton corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <linux/mtd/physmap.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/flash.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+#include <asm/mach-types.h>
+
+#include <mach/regs-serial.h>
+#include <mach/map.h>
+
+#include "cpu.h"
+
+/*NUC900 evb norflash driver data */
+
+#define NUC900_FLASH_BASE 0xA0000000
+#define NUC900_FLASH_SIZE 0x400000
+#define SPIOFFSET 0x200
+#define SPIOREG_SIZE 0x100
+
+static struct mtd_partition nuc900_flash_partitions[] = {
+ {
+ .name = "NOR Partition 1 for kernel (960K)",
+ .size = 0xF0000,
+ .offset = 0x10000,
+ },
+ {
+ .name = "NOR Partition 2 for image (1M)",
+ .size = 0x100000,
+ .offset = 0x100000,
+ },
+ {
+ .name = "NOR Partition 3 for user (2M)",
+ .size = 0x200000,
+ .offset = 0x00200000,
+ }
+};
+
+static struct physmap_flash_data nuc900_flash_data = {
+ .width = 2,
+ .parts = nuc900_flash_partitions,
+ .nr_parts = ARRAY_SIZE(nuc900_flash_partitions),
+};
+
+static struct resource nuc900_flash_resources[] = {
+ {
+ .start = NUC900_FLASH_BASE,
+ .end = NUC900_FLASH_BASE + NUC900_FLASH_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device nuc900_flash_device = {
+ .name = "physmap-flash",
+ .id = 0,
+ .dev = {
+ .platform_data = &nuc900_flash_data,
+ },
+ .resource = nuc900_flash_resources,
+ .num_resources = ARRAY_SIZE(nuc900_flash_resources),
+};
+
+/* USB EHCI Host Controller */
+
+static struct resource nuc900_usb_ehci_resource[] = {
+ [0] = {
+ .start = W90X900_PA_USBEHCIHOST,
+ .end = W90X900_PA_USBEHCIHOST + W90X900_SZ_USBEHCIHOST - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_USBH,
+ .end = IRQ_USBH,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 nuc900_device_usb_ehci_dmamask = 0xffffffffUL;
+
+static struct platform_device nuc900_device_usb_ehci = {
+ .name = "nuc900-ehci",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(nuc900_usb_ehci_resource),
+ .resource = nuc900_usb_ehci_resource,
+ .dev = {
+ .dma_mask = &nuc900_device_usb_ehci_dmamask,
+ .coherent_dma_mask = 0xffffffffUL
+ }
+};
+
+/* USB OHCI Host Controller */
+
+static struct resource nuc900_usb_ohci_resource[] = {
+ [0] = {
+ .start = W90X900_PA_USBOHCIHOST,
+ .end = W90X900_PA_USBOHCIHOST + W90X900_SZ_USBOHCIHOST - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_USBH,
+ .end = IRQ_USBH,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 nuc900_device_usb_ohci_dmamask = 0xffffffffUL;
+static struct platform_device nuc900_device_usb_ohci = {
+ .name = "nuc900-ohci",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(nuc900_usb_ohci_resource),
+ .resource = nuc900_usb_ohci_resource,
+ .dev = {
+ .dma_mask = &nuc900_device_usb_ohci_dmamask,
+ .coherent_dma_mask = 0xffffffffUL
+ }
+};
+
+/* USB Device (Gadget)*/
+
+static struct resource nuc900_usbgadget_resource[] = {
+ [0] = {
+ .start = W90X900_PA_USBDEV,
+ .end = W90X900_PA_USBDEV + W90X900_SZ_USBDEV - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_USBD,
+ .end = IRQ_USBD,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device nuc900_device_usbgadget = {
+ .name = "nuc900-usbgadget",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(nuc900_usbgadget_resource),
+ .resource = nuc900_usbgadget_resource,
+};
+
+/* MAC device */
+
+static struct resource nuc900_emc_resource[] = {
+ [0] = {
+ .start = W90X900_PA_EMC,
+ .end = W90X900_PA_EMC + W90X900_SZ_EMC - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_EMCTX,
+ .end = IRQ_EMCTX,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_EMCRX,
+ .end = IRQ_EMCRX,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static u64 nuc900_device_emc_dmamask = 0xffffffffUL;
+static struct platform_device nuc900_device_emc = {
+ .name = "nuc900-emc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(nuc900_emc_resource),
+ .resource = nuc900_emc_resource,
+ .dev = {
+ .dma_mask = &nuc900_device_emc_dmamask,
+ .coherent_dma_mask = 0xffffffffUL
+ }
+};
+
+/* SPI device */
+
+static struct resource nuc900_spi_resource[] = {
+ [0] = {
+ .start = W90X900_PA_I2C + SPIOFFSET,
+ .end = W90X900_PA_I2C + SPIOFFSET + SPIOREG_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_SSP,
+ .end = IRQ_SSP,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device nuc900_device_spi = {
+ .name = "nuc900-spi",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(nuc900_spi_resource),
+ .resource = nuc900_spi_resource,
+};
+
+/* spi device, spi flash info */
+
+static struct mtd_partition nuc900_spi_flash_partitions[] = {
+ {
+ .name = "bootloader(spi)",
+ .size = 0x0100000,
+ .offset = 0,
+ },
+};
+
+static struct flash_platform_data nuc900_spi_flash_data = {
+ .name = "m25p80",
+ .parts = nuc900_spi_flash_partitions,
+ .nr_parts = ARRAY_SIZE(nuc900_spi_flash_partitions),
+ .type = "w25x16",
+};
+
+static struct spi_board_info nuc900_spi_board_info[] __initdata = {
+ {
+ .modalias = "m25p80",
+ .max_speed_hz = 20000000,
+ .bus_num = 0,
+ .chip_select = 1,
+ .platform_data = &nuc900_spi_flash_data,
+ .mode = SPI_MODE_0,
+ },
+};
+
+/* WDT Device */
+
+static struct resource nuc900_wdt_resource[] = {
+ [0] = {
+ .start = W90X900_PA_TIMER,
+ .end = W90X900_PA_TIMER + W90X900_SZ_TIMER - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_WDT,
+ .end = IRQ_WDT,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device nuc900_device_wdt = {
+ .name = "nuc900-wdt",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(nuc900_wdt_resource),
+ .resource = nuc900_wdt_resource,
+};
+
+/*
+ * public device definition between 910 and 920, or 910
+ * and 950 or 950 and 960...,their dev platform register
+ * should be in specific file such as nuc950, nuc960 c
+ * files rather than the public dev.c file here. so the
+ * corresponding platform_device definition should not be
+ * static.
+*/
+
+/* RTC controller*/
+
+static struct resource nuc900_rtc_resource[] = {
+ [0] = {
+ .start = W90X900_PA_RTC,
+ .end = W90X900_PA_RTC + 0xff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_RTC,
+ .end = IRQ_RTC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device nuc900_device_rtc = {
+ .name = "nuc900-rtc",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(nuc900_rtc_resource),
+ .resource = nuc900_rtc_resource,
+};
+
+/*TouchScreen controller*/
+
+static struct resource nuc900_ts_resource[] = {
+ [0] = {
+ .start = W90X900_PA_ADC,
+ .end = W90X900_PA_ADC + W90X900_SZ_ADC-1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_ADC,
+ .end = IRQ_ADC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device nuc900_device_ts = {
+ .name = "nuc900-ts",
+ .id = -1,
+ .resource = nuc900_ts_resource,
+ .num_resources = ARRAY_SIZE(nuc900_ts_resource),
+};
+
+/* FMI Device */
+
+static struct resource nuc900_fmi_resource[] = {
+ [0] = {
+ .start = W90X900_PA_FMI,
+ .end = W90X900_PA_FMI + W90X900_SZ_FMI - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_FMI,
+ .end = IRQ_FMI,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+struct platform_device nuc900_device_fmi = {
+ .name = "nuc900-fmi",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(nuc900_fmi_resource),
+ .resource = nuc900_fmi_resource,
+};
+
+/* KPI controller*/
+
+static struct resource nuc900_kpi_resource[] = {
+ [0] = {
+ .start = W90X900_PA_KPI,
+ .end = W90X900_PA_KPI + W90X900_SZ_KPI - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_KPI,
+ .end = IRQ_KPI,
+ .flags = IORESOURCE_IRQ,
+ }
+
+};
+
+struct platform_device nuc900_device_kpi = {
+ .name = "nuc900-kpi",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(nuc900_kpi_resource),
+ .resource = nuc900_kpi_resource,
+};
+
+/*Here should be your evb resourse,such as LCD*/
+
+static struct platform_device *nuc900_public_dev[] __initdata = {
+ &nuc900_serial_device,
+ &nuc900_flash_device,
+ &nuc900_device_usb_ehci,
+ &nuc900_device_usb_ohci,
+ &nuc900_device_usbgadget,
+ &nuc900_device_emc,
+ &nuc900_device_spi,
+ &nuc900_device_wdt,
+};
+
+/* Provide adding specific CPU platform devices API */
+
+void __init nuc900_board_init(struct platform_device **device, int size)
+{
+ platform_add_devices(device, size);
+ platform_add_devices(nuc900_public_dev, ARRAY_SIZE(nuc900_public_dev));
+ spi_register_board_info(nuc900_spi_board_info,
+ ARRAY_SIZE(nuc900_spi_board_info));
+}
+
diff --git a/arch/arm/mach-w90x900/gpio.c b/arch/arm/mach-w90x900/gpio.c
index c72e0dfa182..ba05aec7ea4 100644
--- a/arch/arm/mach-w90x900/gpio.c
+++ b/arch/arm/mach-w90x900/gpio.c
@@ -1,7 +1,7 @@
/*
- * linux/arch/arm/mach-w90p910/gpio.c
+ * linux/arch/arm/mach-w90x900/gpio.c
*
- * Generic w90p910 GPIO handling
+ * Generic nuc900 GPIO handling
*
* Wan ZongShun <mcuos.com@gmail.com>
*
@@ -30,31 +30,31 @@
#define GPIO_IN (0x0C)
#define GROUPINERV (0x10)
#define GPIO_GPIO(Nb) (0x00000001 << (Nb))
-#define to_w90p910_gpio_chip(c) container_of(c, struct w90p910_gpio_chip, chip)
+#define to_nuc900_gpio_chip(c) container_of(c, struct nuc900_gpio_chip, chip)
-#define W90P910_GPIO_CHIP(name, base_gpio, nr_gpio) \
+#define NUC900_GPIO_CHIP(name, base_gpio, nr_gpio) \
{ \
.chip = { \
.label = name, \
- .direction_input = w90p910_dir_input, \
- .direction_output = w90p910_dir_output, \
- .get = w90p910_gpio_get, \
- .set = w90p910_gpio_set, \
+ .direction_input = nuc900_dir_input, \
+ .direction_output = nuc900_dir_output, \
+ .get = nuc900_gpio_get, \
+ .set = nuc900_gpio_set, \
.base = base_gpio, \
.ngpio = nr_gpio, \
} \
}
-struct w90p910_gpio_chip {
+struct nuc900_gpio_chip {
struct gpio_chip chip;
void __iomem *regbase; /* Base of group register*/
spinlock_t gpio_lock;
};
-static int w90p910_gpio_get(struct gpio_chip *chip, unsigned offset)
+static int nuc900_gpio_get(struct gpio_chip *chip, unsigned offset)
{
- struct w90p910_gpio_chip *w90p910_gpio = to_w90p910_gpio_chip(chip);
- void __iomem *pio = w90p910_gpio->regbase + GPIO_IN;
+ struct nuc900_gpio_chip *nuc900_gpio = to_nuc900_gpio_chip(chip);
+ void __iomem *pio = nuc900_gpio->regbase + GPIO_IN;
unsigned int regval;
regval = __raw_readl(pio);
@@ -63,14 +63,14 @@ static int w90p910_gpio_get(struct gpio_chip *chip, unsigned offset)
return (regval != 0);
}
-static void w90p910_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
+static void nuc900_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
{
- struct w90p910_gpio_chip *w90p910_gpio = to_w90p910_gpio_chip(chip);
- void __iomem *pio = w90p910_gpio->regbase + GPIO_OUT;
+ struct nuc900_gpio_chip *nuc900_gpio = to_nuc900_gpio_chip(chip);
+ void __iomem *pio = nuc900_gpio->regbase + GPIO_OUT;
unsigned int regval;
unsigned long flags;
- spin_lock_irqsave(&w90p910_gpio->gpio_lock, flags);
+ spin_lock_irqsave(&nuc900_gpio->gpio_lock, flags);
regval = __raw_readl(pio);
@@ -81,36 +81,36 @@ static void w90p910_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
__raw_writel(regval, pio);
- spin_unlock_irqrestore(&w90p910_gpio->gpio_lock, flags);
+ spin_unlock_irqrestore(&nuc900_gpio->gpio_lock, flags);
}
-static int w90p910_dir_input(struct gpio_chip *chip, unsigned offset)
+static int nuc900_dir_input(struct gpio_chip *chip, unsigned offset)
{
- struct w90p910_gpio_chip *w90p910_gpio = to_w90p910_gpio_chip(chip);
- void __iomem *pio = w90p910_gpio->regbase + GPIO_DIR;
+ struct nuc900_gpio_chip *nuc900_gpio = to_nuc900_gpio_chip(chip);
+ void __iomem *pio = nuc900_gpio->regbase + GPIO_DIR;
unsigned int regval;
unsigned long flags;
- spin_lock_irqsave(&w90p910_gpio->gpio_lock, flags);
+ spin_lock_irqsave(&nuc900_gpio->gpio_lock, flags);
regval = __raw_readl(pio);
regval &= ~GPIO_GPIO(offset);
__raw_writel(regval, pio);
- spin_unlock_irqrestore(&w90p910_gpio->gpio_lock, flags);
+ spin_unlock_irqrestore(&nuc900_gpio->gpio_lock, flags);
return 0;
}
-static int w90p910_dir_output(struct gpio_chip *chip, unsigned offset, int val)
+static int nuc900_dir_output(struct gpio_chip *chip, unsigned offset, int val)
{
- struct w90p910_gpio_chip *w90p910_gpio = to_w90p910_gpio_chip(chip);
- void __iomem *outreg = w90p910_gpio->regbase + GPIO_OUT;
- void __iomem *pio = w90p910_gpio->regbase + GPIO_DIR;
+ struct nuc900_gpio_chip *nuc900_gpio = to_nuc900_gpio_chip(chip);
+ void __iomem *outreg = nuc900_gpio->regbase + GPIO_OUT;
+ void __iomem *pio = nuc900_gpio->regbase + GPIO_DIR;
unsigned int regval;
unsigned long flags;
- spin_lock_irqsave(&w90p910_gpio->gpio_lock, flags);
+ spin_lock_irqsave(&nuc900_gpio->gpio_lock, flags);
regval = __raw_readl(pio);
regval |= GPIO_GPIO(offset);
@@ -125,28 +125,28 @@ static int w90p910_dir_output(struct gpio_chip *chip, unsigned offset, int val)
__raw_writel(regval, outreg);
- spin_unlock_irqrestore(&w90p910_gpio->gpio_lock, flags);
+ spin_unlock_irqrestore(&nuc900_gpio->gpio_lock, flags);
return 0;
}
-static struct w90p910_gpio_chip w90p910_gpio[] = {
- W90P910_GPIO_CHIP("GROUPC", 0, 16),
- W90P910_GPIO_CHIP("GROUPD", 16, 10),
- W90P910_GPIO_CHIP("GROUPE", 26, 14),
- W90P910_GPIO_CHIP("GROUPF", 40, 10),
- W90P910_GPIO_CHIP("GROUPG", 50, 17),
- W90P910_GPIO_CHIP("GROUPH", 67, 8),
- W90P910_GPIO_CHIP("GROUPI", 75, 17),
+static struct nuc900_gpio_chip nuc900_gpio[] = {
+ NUC900_GPIO_CHIP("GROUPC", 0, 16),
+ NUC900_GPIO_CHIP("GROUPD", 16, 10),
+ NUC900_GPIO_CHIP("GROUPE", 26, 14),
+ NUC900_GPIO_CHIP("GROUPF", 40, 10),
+ NUC900_GPIO_CHIP("GROUPG", 50, 17),
+ NUC900_GPIO_CHIP("GROUPH", 67, 8),
+ NUC900_GPIO_CHIP("GROUPI", 75, 17),
};
-void __init w90p910_init_gpio(int nr_group)
+void __init nuc900_init_gpio(int nr_group)
{
unsigned i;
- struct w90p910_gpio_chip *gpio_chip;
+ struct nuc900_gpio_chip *gpio_chip;
for (i = 0; i < nr_group; i++) {
- gpio_chip = &w90p910_gpio[i];
+ gpio_chip = &nuc900_gpio[i];
spin_lock_init(&gpio_chip->gpio_lock);
gpio_chip->regbase = GPIO_BASE + i * GROUPINERV;
gpiochip_add(&gpio_chip->chip);
diff --git a/arch/arm/mach-w90x900/include/mach/regs-clock.h b/arch/arm/mach-w90x900/include/mach/regs-clock.h
index f10b6a8dc06..516d6b477b6 100644
--- a/arch/arm/mach-w90x900/include/mach/regs-clock.h
+++ b/arch/arm/mach-w90x900/include/mach/regs-clock.h
@@ -28,4 +28,26 @@
#define REG_CLKEN1 (CLK_BA + 0x24)
#define REG_CLKDIV1 (CLK_BA + 0x28)
+/* Define PLL freq setting */
+#define PLL_DISABLE 0x12B63
+#define PLL_66MHZ 0x2B63
+#define PLL_100MHZ 0x4F64
+#define PLL_120MHZ 0x4F63
+#define PLL_166MHZ 0x4124
+#define PLL_200MHZ 0x4F24
+
+/* Define AHB:CPUFREQ ratio */
+#define AHB_CPUCLK_1_1 0x00
+#define AHB_CPUCLK_1_2 0x01
+#define AHB_CPUCLK_1_4 0x02
+#define AHB_CPUCLK_1_8 0x03
+
+/* Define APB:AHB ratio */
+#define APB_AHB_1_2 0x01
+#define APB_AHB_1_4 0x02
+#define APB_AHB_1_8 0x03
+
+/* Define clock skew */
+#define DEFAULTSKEW 0x48
+
#endif /* __ASM_ARCH_REGS_CLOCK_H */
diff --git a/arch/arm/mach-w90x900/include/mach/regs-ebi.h b/arch/arm/mach-w90x900/include/mach/regs-ebi.h
new file mode 100644
index 00000000000..b68455e7f88
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/regs-ebi.h
@@ -0,0 +1,33 @@
+/*
+ * arch/arm/mach-w90x900/include/mach/regs-ebi.h
+ *
+ * Copyright (c) 2009 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#ifndef __ASM_ARCH_REGS_EBI_H
+#define __ASM_ARCH_REGS_EBI_H
+
+/* EBI Control Registers */
+
+#define EBI_BA W90X900_VA_EBI
+#define REG_EBICON (EBI_BA + 0x00)
+#define REG_ROMCON (EBI_BA + 0x04)
+#define REG_SDCONF0 (EBI_BA + 0x08)
+#define REG_SDCONF1 (EBI_BA + 0x0C)
+#define REG_SDTIME0 (EBI_BA + 0x10)
+#define REG_SDTIME1 (EBI_BA + 0x14)
+#define REG_EXT0CON (EBI_BA + 0x18)
+#define REG_EXT1CON (EBI_BA + 0x1C)
+#define REG_EXT2CON (EBI_BA + 0x20)
+#define REG_EXT3CON (EBI_BA + 0x24)
+#define REG_EXT4CON (EBI_BA + 0x28)
+#define REG_CKSKEW (EBI_BA + 0x2C)
+
+#endif /* __ASM_ARCH_REGS_EBI_H */
diff --git a/arch/arm/mach-w90x900/include/mach/w90p910_keypad.h b/arch/arm/mach-w90x900/include/mach/w90p910_keypad.h
new file mode 100644
index 00000000000..556778e8dda
--- /dev/null
+++ b/arch/arm/mach-w90x900/include/mach/w90p910_keypad.h
@@ -0,0 +1,15 @@
+#ifndef __ASM_ARCH_W90P910_KEYPAD_H
+#define __ASM_ARCH_W90P910_KEYPAD_H
+
+#include <linux/input/matrix_keypad.h>
+
+extern void mfp_set_groupi(struct device *dev);
+
+struct w90p910_keypad_platform_data {
+ const struct matrix_keymap_data *keymap_data;
+
+ unsigned int prescale;
+ unsigned int debounce;
+};
+
+#endif /* __ASM_ARCH_W90P910_KEYPAD_H */
diff --git a/arch/arm/mach-w90x900/irq.c b/arch/arm/mach-w90x900/irq.c
index 0b4fc194729..0ce9d8e867e 100644
--- a/arch/arm/mach-w90x900/irq.c
+++ b/arch/arm/mach-w90x900/irq.c
@@ -10,8 +10,7 @@
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation;version 2 of the License.
*
*/
@@ -29,9 +28,114 @@
#include <mach/hardware.h>
#include <mach/regs-irq.h>
-static void w90x900_irq_mask(unsigned int irq)
+struct group_irq {
+ unsigned long gpen;
+ unsigned int enabled;
+ void (*enable)(struct group_irq *, int enable);
+};
+
+static DEFINE_SPINLOCK(groupirq_lock);
+
+#define DEFINE_GROUP(_name, _ctrlbit, _num) \
+struct group_irq group_##_name = { \
+ .enable = nuc900_group_enable, \
+ .gpen = ((1 << _num) - 1) << _ctrlbit, \
+ }
+
+static void nuc900_group_enable(struct group_irq *gpirq, int enable);
+
+static DEFINE_GROUP(nirq0, 0, 4);
+static DEFINE_GROUP(nirq1, 4, 4);
+static DEFINE_GROUP(usbh, 8, 2);
+static DEFINE_GROUP(ottimer, 16, 3);
+static DEFINE_GROUP(gdma, 20, 2);
+static DEFINE_GROUP(sc, 24, 2);
+static DEFINE_GROUP(i2c, 26, 2);
+static DEFINE_GROUP(ps2, 28, 2);
+
+static int group_irq_enable(struct group_irq *group_irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&groupirq_lock, flags);
+ if (group_irq->enabled++ == 0)
+ (group_irq->enable)(group_irq, 1);
+ spin_unlock_irqrestore(&groupirq_lock, flags);
+
+ return 0;
+}
+
+static void group_irq_disable(struct group_irq *group_irq)
{
+ unsigned long flags;
+
+ WARN_ON(group_irq->enabled == 0);
+
+ spin_lock_irqsave(&groupirq_lock, flags);
+ if (--group_irq->enabled == 0)
+ (group_irq->enable)(group_irq, 0);
+ spin_unlock_irqrestore(&groupirq_lock, flags);
+}
+
+static void nuc900_group_enable(struct group_irq *gpirq, int enable)
+{
+ unsigned int groupen = gpirq->gpen;
+ unsigned long regval;
+
+ regval = __raw_readl(REG_AIC_GEN);
+
+ if (enable)
+ regval |= groupen;
+ else
+ regval &= ~groupen;
+
+ __raw_writel(regval, REG_AIC_GEN);
+}
+
+static void nuc900_irq_mask(unsigned int irq)
+{
+ struct group_irq *group_irq;
+
+ group_irq = NULL;
+
__raw_writel(1 << irq, REG_AIC_MDCR);
+
+ switch (irq) {
+ case IRQ_GROUP0:
+ group_irq = &group_nirq0;
+ break;
+
+ case IRQ_GROUP1:
+ group_irq = &group_nirq1;
+ break;
+
+ case IRQ_USBH:
+ group_irq = &group_usbh;
+ break;
+
+ case IRQ_T_INT_GROUP:
+ group_irq = &group_ottimer;
+ break;
+
+ case IRQ_GDMAGROUP:
+ group_irq = &group_gdma;
+ break;
+
+ case IRQ_SCGROUP:
+ group_irq = &group_sc;
+ break;
+
+ case IRQ_I2CGROUP:
+ group_irq = &group_i2c;
+ break;
+
+ case IRQ_P2SGROUP:
+ group_irq = &group_ps2;
+ break;
+ }
+
+ if (group_irq)
+ group_irq_disable(group_irq);
}
/*
@@ -39,37 +143,71 @@ static void w90x900_irq_mask(unsigned int irq)
* to REG_AIC_EOSCR for ACK
*/
-static void w90x900_irq_ack(unsigned int irq)
+static void nuc900_irq_ack(unsigned int irq)
{
__raw_writel(0x01, REG_AIC_EOSCR);
}
-static void w90x900_irq_unmask(unsigned int irq)
+static void nuc900_irq_unmask(unsigned int irq)
{
- unsigned long mask;
+ struct group_irq *group_irq;
+
+ group_irq = NULL;
- if (irq == IRQ_T_INT_GROUP) {
- mask = __raw_readl(REG_AIC_GEN);
- __raw_writel(TIME_GROUP_IRQ | mask, REG_AIC_GEN);
- __raw_writel(1 << IRQ_T_INT_GROUP, REG_AIC_MECR);
- }
__raw_writel(1 << irq, REG_AIC_MECR);
+
+ switch (irq) {
+ case IRQ_GROUP0:
+ group_irq = &group_nirq0;
+ break;
+
+ case IRQ_GROUP1:
+ group_irq = &group_nirq1;
+ break;
+
+ case IRQ_USBH:
+ group_irq = &group_usbh;
+ break;
+
+ case IRQ_T_INT_GROUP:
+ group_irq = &group_ottimer;
+ break;
+
+ case IRQ_GDMAGROUP:
+ group_irq = &group_gdma;
+ break;
+
+ case IRQ_SCGROUP:
+ group_irq = &group_sc;
+ break;
+
+ case IRQ_I2CGROUP:
+ group_irq = &group_i2c;
+ break;
+
+ case IRQ_P2SGROUP:
+ group_irq = &group_ps2;
+ break;
+ }
+
+ if (group_irq)
+ group_irq_enable(group_irq);
}
-static struct irq_chip w90x900_irq_chip = {
- .ack = w90x900_irq_ack,
- .mask = w90x900_irq_mask,
- .unmask = w90x900_irq_unmask,
+static struct irq_chip nuc900_irq_chip = {
+ .ack = nuc900_irq_ack,
+ .mask = nuc900_irq_mask,
+ .unmask = nuc900_irq_unmask,
};
-void __init w90x900_init_irq(void)
+void __init nuc900_init_irq(void)
{
int irqno;
__raw_writel(0xFFFFFFFE, REG_AIC_MDCR);
for (irqno = IRQ_WDT; irqno <= IRQ_ADC; irqno++) {
- set_irq_chip(irqno, &w90x900_irq_chip);
+ set_irq_chip(irqno, &nuc900_irq_chip);
set_irq_handler(irqno, handle_level_irq);
set_irq_flags(irqno, IRQF_VALID);
}
diff --git a/arch/arm/mach-w90x900/mach-nuc910evb.c b/arch/arm/mach-w90x900/mach-nuc910evb.c
new file mode 100644
index 00000000000..ec05bda946f
--- /dev/null
+++ b/arch/arm/mach-w90x900/mach-nuc910evb.c
@@ -0,0 +1,44 @@
+/*
+ * linux/arch/arm/mach-w90x900/mach-nuc910evb.c
+ *
+ * Based on mach-s3c2410/mach-smdk2410.c by Jonas Dietsche
+ *
+ * Copyright (C) 2008 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach-types.h>
+#include <mach/map.h>
+
+#include "nuc910.h"
+
+static void __init nuc910evb_map_io(void)
+{
+ nuc910_map_io();
+ nuc910_init_clocks();
+}
+
+static void __init nuc910evb_init(void)
+{
+ nuc910_board_init();
+}
+
+MACHINE_START(W90P910EVB, "W90P910EVB")
+ /* Maintainer: Wan ZongShun */
+ .phys_io = W90X900_PA_UART,
+ .io_pg_offst = (((u32)W90X900_VA_UART) >> 18) & 0xfffc,
+ .boot_params = 0,
+ .map_io = nuc910evb_map_io,
+ .init_irq = nuc900_init_irq,
+ .init_machine = nuc910evb_init,
+ .timer = &nuc900_timer,
+MACHINE_END
diff --git a/arch/arm/mach-w90x900/mach-nuc950evb.c b/arch/arm/mach-w90x900/mach-nuc950evb.c
new file mode 100644
index 00000000000..cef903bcccd
--- /dev/null
+++ b/arch/arm/mach-w90x900/mach-nuc950evb.c
@@ -0,0 +1,44 @@
+/*
+ * linux/arch/arm/mach-w90x900/mach-nuc950evb.c
+ *
+ * Based on mach-s3c2410/mach-smdk2410.c by Jonas Dietsche
+ *
+ * Copyright (C) 2008 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach-types.h>
+#include <mach/map.h>
+
+#include "nuc950.h"
+
+static void __init nuc950evb_map_io(void)
+{
+ nuc950_map_io();
+ nuc950_init_clocks();
+}
+
+static void __init nuc950evb_init(void)
+{
+ nuc950_board_init();
+}
+
+MACHINE_START(W90P950EVB, "W90P950EVB")
+ /* Maintainer: Wan ZongShun */
+ .phys_io = W90X900_PA_UART,
+ .io_pg_offst = (((u32)W90X900_VA_UART) >> 18) & 0xfffc,
+ .boot_params = 0,
+ .map_io = nuc950evb_map_io,
+ .init_irq = nuc900_init_irq,
+ .init_machine = nuc950evb_init,
+ .timer = &nuc900_timer,
+MACHINE_END
diff --git a/arch/arm/mach-w90x900/mach-nuc960evb.c b/arch/arm/mach-w90x900/mach-nuc960evb.c
new file mode 100644
index 00000000000..e3a46f19f2b
--- /dev/null
+++ b/arch/arm/mach-w90x900/mach-nuc960evb.c
@@ -0,0 +1,44 @@
+/*
+ * linux/arch/arm/mach-w90x900/mach-nuc960evb.c
+ *
+ * Based on mach-s3c2410/mach-smdk2410.c by Jonas Dietsche
+ *
+ * Copyright (C) 2008 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach-types.h>
+#include <mach/map.h>
+
+#include "nuc960.h"
+
+static void __init nuc960evb_map_io(void)
+{
+ nuc960_map_io();
+ nuc960_init_clocks();
+}
+
+static void __init nuc960evb_init(void)
+{
+ nuc960_board_init();
+}
+
+MACHINE_START(W90N960EVB, "W90N960EVB")
+ /* Maintainer: Wan ZongShun */
+ .phys_io = W90X900_PA_UART,
+ .io_pg_offst = (((u32)W90X900_VA_UART) >> 18) & 0xfffc,
+ .boot_params = 0,
+ .map_io = nuc960evb_map_io,
+ .init_irq = nuc900_init_irq,
+ .init_machine = nuc960evb_init,
+ .timer = &nuc900_timer,
+MACHINE_END
diff --git a/arch/arm/mach-w90x900/mach-w90p910evb.c b/arch/arm/mach-w90x900/mach-w90p910evb.c
deleted file mode 100644
index 7a62bd348e8..00000000000
--- a/arch/arm/mach-w90x900/mach-w90p910evb.c
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * linux/arch/arm/mach-w90x900/mach-w90p910evb.c
- *
- * Based on mach-s3c2410/mach-smdk2410.c by Jonas Dietsche
- *
- * Copyright (C) 2008 Nuvoton technology corporation.
- *
- * Wan ZongShun <mcuos.com@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation;version 2 of the License.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/list.h>
-#include <linux/timer.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/mtd/physmap.h>
-
-#include <asm/mach/arch.h>
-#include <asm/mach/map.h>
-#include <asm/mach/irq.h>
-#include <asm/mach-types.h>
-
-#include <mach/regs-serial.h>
-#include <mach/map.h>
-
-#include "cpu.h"
-/*w90p910 evb norflash driver data */
-
-#define W90P910_FLASH_BASE 0xA0000000
-#define W90P910_FLASH_SIZE 0x400000
-
-static struct mtd_partition w90p910_flash_partitions[] = {
- {
- .name = "NOR Partition 1 for kernel (960K)",
- .size = 0xF0000,
- .offset = 0x10000,
- },
- {
- .name = "NOR Partition 2 for image (1M)",
- .size = 0x100000,
- .offset = 0x100000,
- },
- {
- .name = "NOR Partition 3 for user (2M)",
- .size = 0x200000,
- .offset = 0x00200000,
- }
-};
-
-static struct physmap_flash_data w90p910_flash_data = {
- .width = 2,
- .parts = w90p910_flash_partitions,
- .nr_parts = ARRAY_SIZE(w90p910_flash_partitions),
-};
-
-static struct resource w90p910_flash_resources[] = {
- {
- .start = W90P910_FLASH_BASE,
- .end = W90P910_FLASH_BASE + W90P910_FLASH_SIZE - 1,
- .flags = IORESOURCE_MEM,
- }
-};
-
-static struct platform_device w90p910_flash_device = {
- .name = "physmap-flash",
- .id = 0,
- .dev = {
- .platform_data = &w90p910_flash_data,
- },
- .resource = w90p910_flash_resources,
- .num_resources = ARRAY_SIZE(w90p910_flash_resources),
-};
-
-/* USB EHCI Host Controller */
-
-static struct resource w90x900_usb_ehci_resource[] = {
- [0] = {
- .start = W90X900_PA_USBEHCIHOST,
- .end = W90X900_PA_USBEHCIHOST + W90X900_SZ_USBEHCIHOST - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_USBH,
- .end = IRQ_USBH,
- .flags = IORESOURCE_IRQ,
- }
-};
-
-static u64 w90x900_device_usb_ehci_dmamask = 0xffffffffUL;
-
-struct platform_device w90x900_device_usb_ehci = {
- .name = "w90x900-ehci",
- .id = -1,
- .num_resources = ARRAY_SIZE(w90x900_usb_ehci_resource),
- .resource = w90x900_usb_ehci_resource,
- .dev = {
- .dma_mask = &w90x900_device_usb_ehci_dmamask,
- .coherent_dma_mask = 0xffffffffUL
- }
-};
-EXPORT_SYMBOL(w90x900_device_usb_ehci);
-
-/* USB OHCI Host Controller */
-
-static struct resource w90x900_usb_ohci_resource[] = {
- [0] = {
- .start = W90X900_PA_USBOHCIHOST,
- .end = W90X900_PA_USBOHCIHOST + W90X900_SZ_USBOHCIHOST - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_USBH,
- .end = IRQ_USBH,
- .flags = IORESOURCE_IRQ,
- }
-};
-
-static u64 w90x900_device_usb_ohci_dmamask = 0xffffffffUL;
-struct platform_device w90x900_device_usb_ohci = {
- .name = "w90x900-ohci",
- .id = -1,
- .num_resources = ARRAY_SIZE(w90x900_usb_ohci_resource),
- .resource = w90x900_usb_ohci_resource,
- .dev = {
- .dma_mask = &w90x900_device_usb_ohci_dmamask,
- .coherent_dma_mask = 0xffffffffUL
- }
-};
-EXPORT_SYMBOL(w90x900_device_usb_ohci);
-
-/*TouchScreen controller*/
-
-static struct resource w90x900_ts_resource[] = {
- [0] = {
- .start = W90X900_PA_ADC,
- .end = W90X900_PA_ADC + W90X900_SZ_ADC-1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_ADC,
- .end = IRQ_ADC,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct platform_device w90x900_device_ts = {
- .name = "w90x900-ts",
- .id = -1,
- .resource = w90x900_ts_resource,
- .num_resources = ARRAY_SIZE(w90x900_ts_resource),
-};
-EXPORT_SYMBOL(w90x900_device_ts);
-
-/* RTC controller*/
-
-static struct resource w90x900_rtc_resource[] = {
- [0] = {
- .start = W90X900_PA_RTC,
- .end = W90X900_PA_RTC + 0xff,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_RTC,
- .end = IRQ_RTC,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct platform_device w90x900_device_rtc = {
- .name = "w90x900-rtc",
- .id = -1,
- .num_resources = ARRAY_SIZE(w90x900_rtc_resource),
- .resource = w90x900_rtc_resource,
-};
-EXPORT_SYMBOL(w90x900_device_rtc);
-
-/* KPI controller*/
-
-static struct resource w90x900_kpi_resource[] = {
- [0] = {
- .start = W90X900_PA_KPI,
- .end = W90X900_PA_KPI + W90X900_SZ_KPI - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_KPI,
- .end = IRQ_KPI,
- .flags = IORESOURCE_IRQ,
- }
-
-};
-
-struct platform_device w90x900_device_kpi = {
- .name = "w90x900-kpi",
- .id = -1,
- .num_resources = ARRAY_SIZE(w90x900_kpi_resource),
- .resource = w90x900_kpi_resource,
-};
-EXPORT_SYMBOL(w90x900_device_kpi);
-
-/* USB Device (Gadget)*/
-
-static struct resource w90x900_usbgadget_resource[] = {
- [0] = {
- .start = W90X900_PA_USBDEV,
- .end = W90X900_PA_USBDEV + W90X900_SZ_USBDEV - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_USBD,
- .end = IRQ_USBD,
- .flags = IORESOURCE_IRQ,
- }
-};
-
-struct platform_device w90x900_device_usbgadget = {
- .name = "w90x900-usbgadget",
- .id = -1,
- .num_resources = ARRAY_SIZE(w90x900_usbgadget_resource),
- .resource = w90x900_usbgadget_resource,
-};
-EXPORT_SYMBOL(w90x900_device_usbgadget);
-
-static struct map_desc w90p910_iodesc[] __initdata = {
-};
-
-/*Here should be your evb resourse,such as LCD*/
-
-static struct platform_device *w90p910evb_dev[] __initdata = {
- &w90p910_serial_device,
- &w90p910_flash_device,
- &w90x900_device_usb_ehci,
- &w90x900_device_usb_ohci,
- &w90x900_device_ts,
- &w90x900_device_rtc,
- &w90x900_device_kpi,
- &w90x900_device_usbgadget,
-};
-
-static void __init w90p910evb_map_io(void)
-{
- w90p910_map_io(w90p910_iodesc, ARRAY_SIZE(w90p910_iodesc));
- w90p910_init_clocks();
-}
-
-static void __init w90p910evb_init(void)
-{
- platform_add_devices(w90p910evb_dev, ARRAY_SIZE(w90p910evb_dev));
-}
-
-MACHINE_START(W90P910EVB, "W90P910EVB")
- /* Maintainer: Wan ZongShun */
- .phys_io = W90X900_PA_UART,
- .io_pg_offst = (((u32)W90X900_VA_UART) >> 18) & 0xfffc,
- .boot_params = 0,
- .map_io = w90p910evb_map_io,
- .init_irq = w90x900_init_irq,
- .init_machine = w90p910evb_init,
- .timer = &w90x900_timer,
-MACHINE_END
diff --git a/arch/arm/mach-w90x900/mfp-w90p910.c b/arch/arm/mach-w90x900/mfp-w90p910.c
deleted file mode 100644
index a3520fefb5e..00000000000
--- a/arch/arm/mach-w90x900/mfp-w90p910.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * linux/arch/arm/mach-w90x900/mfp-w90p910.c
- *
- * Copyright (c) 2008 Nuvoton technology corporation
- *
- * Wan ZongShun <mcuos.com@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation;version 2 of the License.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/device.h>
-#include <linux/list.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/string.h>
-#include <linux/clk.h>
-#include <linux/mutex.h>
-#include <linux/io.h>
-
-#include <mach/hardware.h>
-
-#define REG_MFSEL (W90X900_VA_GCR + 0xC)
-
-#define GPSELF (0x01 << 1)
-
-#define GPSELC (0x03 << 2)
-#define ENKPI (0x02 << 2)
-#define ENNAND (0x01 << 2)
-
-#define GPSELEI0 (0x01 << 26)
-#define GPSELEI1 (0x01 << 27)
-
-static DECLARE_MUTEX(mfp_sem);
-
-void mfp_set_groupf(struct device *dev)
-{
- unsigned long mfpen;
- const char *dev_id;
-
- BUG_ON(!dev);
-
- down(&mfp_sem);
-
- dev_id = dev_name(dev);
-
- mfpen = __raw_readl(REG_MFSEL);
-
- if (strcmp(dev_id, "w90p910-emc") == 0)
- mfpen |= GPSELF;/*enable mac*/
- else
- mfpen &= ~GPSELF;/*GPIOF[9:0]*/
-
- __raw_writel(mfpen, REG_MFSEL);
-
- up(&mfp_sem);
-}
-EXPORT_SYMBOL(mfp_set_groupf);
-
-void mfp_set_groupc(struct device *dev)
-{
- unsigned long mfpen;
- const char *dev_id;
-
- BUG_ON(!dev);
-
- down(&mfp_sem);
-
- dev_id = dev_name(dev);
-
- mfpen = __raw_readl(REG_MFSEL);
-
- if (strcmp(dev_id, "w90p910-lcd") == 0)
- mfpen |= GPSELC;/*enable lcd*/
- else if (strcmp(dev_id, "w90p910-kpi") == 0) {
- mfpen &= (~GPSELC);/*enable kpi*/
- mfpen |= ENKPI;
- } else if (strcmp(dev_id, "w90p910-nand") == 0) {
- mfpen &= (~GPSELC);/*enable nand*/
- mfpen |= ENNAND;
- } else
- mfpen &= (~GPSELC);/*GPIOC[14:0]*/
-
- __raw_writel(mfpen, REG_MFSEL);
-
- up(&mfp_sem);
-}
-EXPORT_SYMBOL(mfp_set_groupc);
-
-void mfp_set_groupi(struct device *dev, int gpio)
-{
- unsigned long mfpen;
- const char *dev_id;
-
- BUG_ON(!dev);
-
- down(&mfp_sem);
-
- dev_id = dev_name(dev);
-
- mfpen = __raw_readl(REG_MFSEL);
-
- if (strcmp(dev_id, "w90p910-wdog") == 0)
- mfpen |= GPSELEI1;/*enable wdog*/
- else if (strcmp(dev_id, "w90p910-atapi") == 0)
- mfpen |= GPSELEI0;/*enable atapi*/
-
- __raw_writel(mfpen, REG_MFSEL);
-
- up(&mfp_sem);
-}
-EXPORT_SYMBOL(mfp_set_groupi);
-
diff --git a/arch/arm/mach-w90x900/mfp.c b/arch/arm/mach-w90x900/mfp.c
new file mode 100644
index 00000000000..a47dc9a708e
--- /dev/null
+++ b/arch/arm/mach-w90x900/mfp.c
@@ -0,0 +1,158 @@
+/*
+ * linux/arch/arm/mach-w90x900/mfp.c
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+
+#define REG_MFSEL (W90X900_VA_GCR + 0xC)
+
+#define GPSELF (0x01 << 1)
+
+#define GPSELC (0x03 << 2)
+#define ENKPI (0x02 << 2)
+#define ENNAND (0x01 << 2)
+
+#define GPSELEI0 (0x01 << 26)
+#define GPSELEI1 (0x01 << 27)
+
+#define GPIOG0TO1 (0x03 << 14)
+#define GPIOG2TO3 (0x03 << 16)
+#define ENSPI (0x0a << 14)
+#define ENI2C0 (0x01 << 14)
+#define ENI2C1 (0x01 << 16)
+
+static DEFINE_MUTEX(mfp_mutex);
+
+void mfp_set_groupf(struct device *dev)
+{
+ unsigned long mfpen;
+ const char *dev_id;
+
+ BUG_ON(!dev);
+
+ mutex_lock(&mfp_mutex);
+
+ dev_id = dev_name(dev);
+
+ mfpen = __raw_readl(REG_MFSEL);
+
+ if (strcmp(dev_id, "nuc900-emc") == 0)
+ mfpen |= GPSELF;/*enable mac*/
+ else
+ mfpen &= ~GPSELF;/*GPIOF[9:0]*/
+
+ __raw_writel(mfpen, REG_MFSEL);
+
+ mutex_unlock(&mfp_mutex);
+}
+EXPORT_SYMBOL(mfp_set_groupf);
+
+void mfp_set_groupc(struct device *dev)
+{
+ unsigned long mfpen;
+ const char *dev_id;
+
+ BUG_ON(!dev);
+
+ mutex_lock(&mfp_mutex);
+
+ dev_id = dev_name(dev);
+
+ mfpen = __raw_readl(REG_MFSEL);
+
+ if (strcmp(dev_id, "nuc900-lcd") == 0)
+ mfpen |= GPSELC;/*enable lcd*/
+ else if (strcmp(dev_id, "nuc900-kpi") == 0) {
+ mfpen &= (~GPSELC);/*enable kpi*/
+ mfpen |= ENKPI;
+ } else if (strcmp(dev_id, "nuc900-nand") == 0) {
+ mfpen &= (~GPSELC);/*enable nand*/
+ mfpen |= ENNAND;
+ } else
+ mfpen &= (~GPSELC);/*GPIOC[14:0]*/
+
+ __raw_writel(mfpen, REG_MFSEL);
+
+ mutex_unlock(&mfp_mutex);
+}
+EXPORT_SYMBOL(mfp_set_groupc);
+
+void mfp_set_groupi(struct device *dev)
+{
+ unsigned long mfpen;
+ const char *dev_id;
+
+ BUG_ON(!dev);
+
+ mutex_lock(&mfp_mutex);
+
+ dev_id = dev_name(dev);
+
+ mfpen = __raw_readl(REG_MFSEL);
+
+ mfpen &= ~GPSELEI1;/*default gpio16*/
+
+ if (strcmp(dev_id, "nuc900-wdog") == 0)
+ mfpen |= GPSELEI1;/*enable wdog*/
+ else if (strcmp(dev_id, "nuc900-atapi") == 0)
+ mfpen |= GPSELEI0;/*enable atapi*/
+ else if (strcmp(dev_id, "nuc900-keypad") == 0)
+ mfpen &= ~GPSELEI0;/*enable keypad*/
+
+ __raw_writel(mfpen, REG_MFSEL);
+
+ mutex_unlock(&mfp_mutex);
+}
+EXPORT_SYMBOL(mfp_set_groupi);
+
+void mfp_set_groupg(struct device *dev)
+{
+ unsigned long mfpen;
+ const char *dev_id;
+
+ BUG_ON(!dev);
+
+ mutex_lock(&mfp_mutex);
+
+ dev_id = dev_name(dev);
+
+ mfpen = __raw_readl(REG_MFSEL);
+
+ if (strcmp(dev_id, "nuc900-spi") == 0) {
+ mfpen &= ~(GPIOG0TO1 | GPIOG2TO3);
+ mfpen |= ENSPI;/*enable spi*/
+ } else if (strcmp(dev_id, "nuc900-i2c0") == 0) {
+ mfpen &= ~(GPIOG0TO1);
+ mfpen |= ENI2C0;/*enable i2c0*/
+ } else if (strcmp(dev_id, "nuc900-i2c1") == 0) {
+ mfpen &= ~(GPIOG2TO3);
+ mfpen |= ENI2C1;/*enable i2c1*/
+ } else {
+ mfpen &= ~(GPIOG0TO1 | GPIOG2TO3);/*GPIOG[3:0]*/
+ }
+
+ __raw_writel(mfpen, REG_MFSEL);
+
+ mutex_unlock(&mfp_mutex);
+}
+EXPORT_SYMBOL(mfp_set_groupg);
+
diff --git a/arch/arm/mach-w90x900/nuc910.c b/arch/arm/mach-w90x900/nuc910.c
new file mode 100644
index 00000000000..656f03b3b62
--- /dev/null
+++ b/arch/arm/mach-w90x900/nuc910.c
@@ -0,0 +1,60 @@
+/*
+ * linux/arch/arm/mach-w90x900/nuc910.c
+ *
+ * Based on linux/arch/arm/plat-s3c24xx/s3c244x.c by Ben Dooks
+ *
+ * Copyright (c) 2009 Nuvoton corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * NUC910 cpu support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <asm/mach/map.h>
+#include <mach/hardware.h>
+#include "cpu.h"
+#include "clock.h"
+
+/* define specific CPU platform device */
+
+static struct platform_device *nuc910_dev[] __initdata = {
+ &nuc900_device_ts,
+ &nuc900_device_rtc,
+};
+
+/* define specific CPU platform io map */
+
+static struct map_desc nuc910evb_iodesc[] __initdata = {
+ IODESC_ENT(USBEHCIHOST),
+ IODESC_ENT(USBOHCIHOST),
+ IODESC_ENT(KPI),
+ IODESC_ENT(USBDEV),
+ IODESC_ENT(ADC),
+};
+
+/*Init NUC910 evb io*/
+
+void __init nuc910_map_io(void)
+{
+ nuc900_map_io(nuc910evb_iodesc, ARRAY_SIZE(nuc910evb_iodesc));
+}
+
+/*Init NUC910 clock*/
+
+void __init nuc910_init_clocks(void)
+{
+ nuc900_init_clocks();
+}
+
+/*Init NUC910 board info*/
+
+void __init nuc910_board_init(void)
+{
+ nuc900_board_init(nuc910_dev, ARRAY_SIZE(nuc910_dev));
+}
diff --git a/arch/arm/mach-w90x900/nuc910.h b/arch/arm/mach-w90x900/nuc910.h
new file mode 100644
index 00000000000..83e9ba5fc26
--- /dev/null
+++ b/arch/arm/mach-w90x900/nuc910.h
@@ -0,0 +1,28 @@
+/*
+ * arch/arm/mach-w90x900/nuc910.h
+ *
+ * Copyright (c) 2008 Nuvoton corporation
+ *
+ * Header file for NUC900 CPU support
+ *
+ * Wan ZongShun <mcuos.com@gmail.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.
+ *
+ */
+
+struct map_desc;
+struct sys_timer;
+
+/* core initialisation functions */
+
+extern void nuc900_init_irq(void);
+extern struct sys_timer nuc900_timer;
+
+/* extern file from nuc910.c */
+
+extern void nuc910_board_init(void);
+extern void nuc910_init_clocks(void);
+extern void nuc910_map_io(void);
diff --git a/arch/arm/mach-w90x900/nuc950.c b/arch/arm/mach-w90x900/nuc950.c
new file mode 100644
index 00000000000..149508116d1
--- /dev/null
+++ b/arch/arm/mach-w90x900/nuc950.c
@@ -0,0 +1,54 @@
+/*
+ * linux/arch/arm/mach-w90x900/nuc950.c
+ *
+ * Based on linux/arch/arm/plat-s3c24xx/s3c244x.c by Ben Dooks
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * NUC950 cpu support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <asm/mach/map.h>
+#include <mach/hardware.h>
+#include "cpu.h"
+
+/* define specific CPU platform device */
+
+static struct platform_device *nuc950_dev[] __initdata = {
+ &nuc900_device_kpi,
+ &nuc900_device_fmi,
+};
+
+/* define specific CPU platform io map */
+
+static struct map_desc nuc950evb_iodesc[] __initdata = {
+};
+
+/*Init NUC950 evb io*/
+
+void __init nuc950_map_io(void)
+{
+ nuc900_map_io(nuc950evb_iodesc, ARRAY_SIZE(nuc950evb_iodesc));
+}
+
+/*Init NUC950 clock*/
+
+void __init nuc950_init_clocks(void)
+{
+ nuc900_init_clocks();
+}
+
+/*Init NUC950 board info*/
+
+void __init nuc950_board_init(void)
+{
+ nuc900_board_init(nuc950_dev, ARRAY_SIZE(nuc950_dev));
+}
diff --git a/arch/arm/mach-w90x900/nuc950.h b/arch/arm/mach-w90x900/nuc950.h
new file mode 100644
index 00000000000..98a1148bc5a
--- /dev/null
+++ b/arch/arm/mach-w90x900/nuc950.h
@@ -0,0 +1,28 @@
+/*
+ * arch/arm/mach-w90x900/nuc950.h
+ *
+ * Copyright (c) 2008 Nuvoton corporation
+ *
+ * Header file for NUC900 CPU support
+ *
+ * Wan ZongShun <mcuos.com@gmail.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.
+ *
+ */
+
+struct map_desc;
+struct sys_timer;
+
+/* core initialisation functions */
+
+extern void nuc900_init_irq(void);
+extern struct sys_timer nuc900_timer;
+
+/* extern file from nuc950.c */
+
+extern void nuc950_board_init(void);
+extern void nuc950_init_clocks(void);
+extern void nuc950_map_io(void);
diff --git a/arch/arm/mach-w90x900/nuc960.c b/arch/arm/mach-w90x900/nuc960.c
new file mode 100644
index 00000000000..8851a3a27ce
--- /dev/null
+++ b/arch/arm/mach-w90x900/nuc960.c
@@ -0,0 +1,54 @@
+/*
+ * linux/arch/arm/mach-w90x900/nuc960.c
+ *
+ * Based on linux/arch/arm/plat-s3c24xx/s3c244x.c by Ben Dooks
+ *
+ * Copyright (c) 2008 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * NUC960 cpu support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <asm/mach/map.h>
+#include <mach/hardware.h>
+#include "cpu.h"
+
+/* define specific CPU platform device */
+
+static struct platform_device *nuc960_dev[] __initdata = {
+ &nuc900_device_kpi,
+ &nuc900_device_fmi,
+};
+
+/* define specific CPU platform io map */
+
+static struct map_desc nuc960evb_iodesc[] __initdata = {
+};
+
+/*Init NUC960 evb io*/
+
+void __init nuc960_map_io(void)
+{
+ nuc900_map_io(nuc960evb_iodesc, ARRAY_SIZE(nuc960evb_iodesc));
+}
+
+/*Init NUC960 clock*/
+
+void __init nuc960_init_clocks(void)
+{
+ nuc900_init_clocks();
+}
+
+/*Init NUC960 board info*/
+
+void __init nuc960_board_init(void)
+{
+ nuc900_board_init(nuc960_dev, ARRAY_SIZE(nuc960_dev));
+}
diff --git a/arch/arm/mach-w90x900/nuc960.h b/arch/arm/mach-w90x900/nuc960.h
new file mode 100644
index 00000000000..f0c07cbe3a8
--- /dev/null
+++ b/arch/arm/mach-w90x900/nuc960.h
@@ -0,0 +1,28 @@
+/*
+ * arch/arm/mach-w90x900/nuc960.h
+ *
+ * Copyright (c) 2008 Nuvoton corporation
+ *
+ * Header file for NUC900 CPU support
+ *
+ * Wan ZongShun <mcuos.com@gmail.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.
+ *
+ */
+
+struct map_desc;
+struct sys_timer;
+
+/* core initialisation functions */
+
+extern void nuc900_init_irq(void);
+extern struct sys_timer nuc900_timer;
+
+/* extern file from nuc960.c */
+
+extern void nuc960_board_init(void);
+extern void nuc960_init_clocks(void);
+extern void nuc960_map_io(void);
diff --git a/arch/arm/mach-w90x900/time.c b/arch/arm/mach-w90x900/time.c
index bcc838f6b39..4128af870b4 100644
--- a/arch/arm/mach-w90x900/time.c
+++ b/arch/arm/mach-w90x900/time.c
@@ -3,7 +3,7 @@
*
* Based on linux/arch/arm/plat-s3c24xx/time.c by Ben Dooks
*
- * Copyright (c) 2008 Nuvoton technology corporation
+ * Copyright (c) 2009 Nuvoton technology corporation
* All rights reserved.
*
* Wan ZongShun <mcuos.com@gmail.com>
@@ -23,6 +23,8 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/leds.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
#include <asm/mach-types.h>
#include <asm/mach/irq.h>
@@ -31,49 +33,150 @@
#include <mach/map.h>
#include <mach/regs-timer.h>
-static unsigned long w90x900_gettimeoffset(void)
+#define RESETINT 0x1f
+#define PERIOD (0x01 << 27)
+#define ONESHOT (0x00 << 27)
+#define COUNTEN (0x01 << 30)
+#define INTEN (0x01 << 29)
+
+#define TICKS_PER_SEC 100
+#define PRESCALE 0x63 /* Divider = prescale + 1 */
+
+unsigned int timer0_load;
+
+static void nuc900_clockevent_setmode(enum clock_event_mode mode,
+ struct clock_event_device *clk)
{
+ unsigned int val;
+
+ val = __raw_readl(REG_TCSR0);
+ val &= ~(0x03 << 27);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ __raw_writel(timer0_load, REG_TICR0);
+ val |= (PERIOD | COUNTEN | INTEN | PRESCALE);
+ break;
+
+ case CLOCK_EVT_MODE_ONESHOT:
+ val |= (ONESHOT | COUNTEN | INTEN | PRESCALE);
+ break;
+
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_RESUME:
+ break;
+ }
+
+ __raw_writel(val, REG_TCSR0);
+}
+
+static int nuc900_clockevent_setnextevent(unsigned long evt,
+ struct clock_event_device *clk)
+{
+ unsigned int val;
+
+ __raw_writel(evt, REG_TICR0);
+
+ val = __raw_readl(REG_TCSR0);
+ val |= (COUNTEN | INTEN | PRESCALE);
+ __raw_writel(val, REG_TCSR0);
+
return 0;
}
+static struct clock_event_device nuc900_clockevent_device = {
+ .name = "nuc900-timer0",
+ .shift = 32,
+ .features = CLOCK_EVT_MODE_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+ .set_mode = nuc900_clockevent_setmode,
+ .set_next_event = nuc900_clockevent_setnextevent,
+ .rating = 300,
+};
+
/*IRQ handler for the timer*/
-static irqreturn_t
-w90x900_timer_interrupt(int irq, void *dev_id)
+static irqreturn_t nuc900_timer0_interrupt(int irq, void *dev_id)
{
- timer_tick();
+ struct clock_event_device *evt = &nuc900_clockevent_device;
+
__raw_writel(0x01, REG_TISR); /* clear TIF0 */
+
+ evt->event_handler(evt);
return IRQ_HANDLED;
}
-static struct irqaction w90x900_timer_irq = {
- .name = "w90x900 Timer Tick",
+static struct irqaction nuc900_timer0_irq = {
+ .name = "nuc900-timer0",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
- .handler = w90x900_timer_interrupt,
+ .handler = nuc900_timer0_interrupt,
};
-/*Set up timer reg.*/
+static void __init nuc900_clockevents_init(unsigned int rate)
+{
+ nuc900_clockevent_device.mult = div_sc(rate, NSEC_PER_SEC,
+ nuc900_clockevent_device.shift);
+ nuc900_clockevent_device.max_delta_ns = clockevent_delta2ns(0xffffffff,
+ &nuc900_clockevent_device);
+ nuc900_clockevent_device.min_delta_ns = clockevent_delta2ns(0xf,
+ &nuc900_clockevent_device);
+ nuc900_clockevent_device.cpumask = cpumask_of(0);
-static void w90x900_timer_setup(void)
+ clockevents_register_device(&nuc900_clockevent_device);
+}
+
+static cycle_t nuc900_get_cycles(struct clocksource *cs)
{
- __raw_writel(0, REG_TCSR0);
- __raw_writel(0, REG_TCSR1);
- __raw_writel(0, REG_TCSR2);
- __raw_writel(0, REG_TCSR3);
- __raw_writel(0, REG_TCSR4);
- __raw_writel(0x1F, REG_TISR);
- __raw_writel(15000000/(100 * 100), REG_TICR0);
- __raw_writel(0x68000063, REG_TCSR0);
+ return ~__raw_readl(REG_TDR1);
}
-static void __init w90x900_timer_init(void)
+static struct clocksource clocksource_nuc900 = {
+ .name = "nuc900-timer1",
+ .rating = 200,
+ .read = nuc900_get_cycles,
+ .mask = CLOCKSOURCE_MASK(32),
+ .shift = 20,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static void __init nuc900_clocksource_init(unsigned int rate)
{
- w90x900_timer_setup();
- setup_irq(IRQ_TIMER0, &w90x900_timer_irq);
+ unsigned int val;
+
+ __raw_writel(0xffffffff, REG_TICR1);
+
+ val = __raw_readl(REG_TCSR1);
+ val |= (COUNTEN | PERIOD);
+ __raw_writel(val, REG_TCSR1);
+
+ clocksource_nuc900.mult =
+ clocksource_khz2mult((rate / 1000), clocksource_nuc900.shift);
+ clocksource_register(&clocksource_nuc900);
+}
+
+static void __init nuc900_timer_init(void)
+{
+ struct clk *ck_ext = clk_get(NULL, "ext");
+ unsigned int rate;
+
+ BUG_ON(IS_ERR(ck_ext));
+
+ rate = clk_get_rate(ck_ext);
+ clk_put(ck_ext);
+ rate = rate / (PRESCALE + 0x01);
+
+ /* set a known state */
+ __raw_writel(0x00, REG_TCSR0);
+ __raw_writel(0x00, REG_TCSR1);
+ __raw_writel(RESETINT, REG_TISR);
+ timer0_load = (rate / TICKS_PER_SEC);
+
+ setup_irq(IRQ_TIMER0, &nuc900_timer0_irq);
+
+ nuc900_clocksource_init(rate);
+ nuc900_clockevents_init(rate);
}
-struct sys_timer w90x900_timer = {
- .init = w90x900_timer_init,
- .offset = w90x900_gettimeoffset,
- .resume = w90x900_timer_setup
+struct sys_timer nuc900_timer = {
+ .init = nuc900_timer_init,
};
diff --git a/arch/arm/mach-w90x900/w90p910.c b/arch/arm/mach-w90x900/w90p910.c
deleted file mode 100644
index 1c97e4930b7..00000000000
--- a/arch/arm/mach-w90x900/w90p910.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * linux/arch/arm/mach-w90x900/w90p910.c
- *
- * Based on linux/arch/arm/plat-s3c24xx/s3c244x.c by Ben Dooks
- *
- * Copyright (c) 2008 Nuvoton technology corporation.
- *
- * Wan ZongShun <mcuos.com@gmail.com>
- *
- * W90P910 cpu support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation;version 2 of the License.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/interrupt.h>
-#include <linux/list.h>
-#include <linux/timer.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/serial_8250.h>
-
-#include <asm/mach/arch.h>
-#include <asm/mach/map.h>
-#include <asm/mach/irq.h>
-#include <asm/irq.h>
-
-#include <mach/hardware.h>
-#include <mach/regs-serial.h>
-
-#include "cpu.h"
-#include "clock.h"
-
-/* Initial IO mappings */
-
-static struct map_desc w90p910_iodesc[] __initdata = {
- IODESC_ENT(IRQ),
- IODESC_ENT(GCR),
- IODESC_ENT(UART),
- IODESC_ENT(TIMER),
- IODESC_ENT(EBI),
- IODESC_ENT(USBEHCIHOST),
- IODESC_ENT(USBOHCIHOST),
- IODESC_ENT(ADC),
- IODESC_ENT(RTC),
- IODESC_ENT(KPI),
- IODESC_ENT(USBDEV),
- /*IODESC_ENT(LCD),*/
-};
-
-/* Initial clock declarations. */
-static DEFINE_CLK(lcd, 0);
-static DEFINE_CLK(audio, 1);
-static DEFINE_CLK(fmi, 4);
-static DEFINE_CLK(dmac, 5);
-static DEFINE_CLK(atapi, 6);
-static DEFINE_CLK(emc, 7);
-static DEFINE_CLK(usbd, 8);
-static DEFINE_CLK(usbh, 9);
-static DEFINE_CLK(g2d, 10);;
-static DEFINE_CLK(pwm, 18);
-static DEFINE_CLK(ps2, 24);
-static DEFINE_CLK(kpi, 25);
-static DEFINE_CLK(wdt, 26);
-static DEFINE_CLK(gdma, 27);
-static DEFINE_CLK(adc, 28);
-static DEFINE_CLK(usi, 29);
-
-static struct clk_lookup w90p910_clkregs[] = {
- DEF_CLKLOOK(&clk_lcd, "w90p910-lcd", NULL),
- DEF_CLKLOOK(&clk_audio, "w90p910-audio", NULL),
- DEF_CLKLOOK(&clk_fmi, "w90p910-fmi", NULL),
- DEF_CLKLOOK(&clk_dmac, "w90p910-dmac", NULL),
- DEF_CLKLOOK(&clk_atapi, "w90p910-atapi", NULL),
- DEF_CLKLOOK(&clk_emc, "w90p910-emc", NULL),
- DEF_CLKLOOK(&clk_usbd, "w90p910-usbd", NULL),
- DEF_CLKLOOK(&clk_usbh, "w90p910-usbh", NULL),
- DEF_CLKLOOK(&clk_g2d, "w90p910-g2d", NULL),
- DEF_CLKLOOK(&clk_pwm, "w90p910-pwm", NULL),
- DEF_CLKLOOK(&clk_ps2, "w90p910-ps2", NULL),
- DEF_CLKLOOK(&clk_kpi, "w90p910-kpi", NULL),
- DEF_CLKLOOK(&clk_wdt, "w90p910-wdt", NULL),
- DEF_CLKLOOK(&clk_gdma, "w90p910-gdma", NULL),
- DEF_CLKLOOK(&clk_adc, "w90p910-adc", NULL),
- DEF_CLKLOOK(&clk_usi, "w90p910-usi", NULL),
-};
-
-/* Initial serial platform data */
-
-struct plat_serial8250_port w90p910_uart_data[] = {
- W90X900_8250PORT(UART0),
-};
-
-struct platform_device w90p910_serial_device = {
- .name = "serial8250",
- .id = PLAT8250_DEV_PLATFORM,
- .dev = {
- .platform_data = w90p910_uart_data,
- },
-};
-
-/*Init W90P910 evb io*/
-
-void __init w90p910_map_io(struct map_desc *mach_desc, int mach_size)
-{
- unsigned long idcode = 0x0;
-
- iotable_init(w90p910_iodesc, ARRAY_SIZE(w90p910_iodesc));
-
- idcode = __raw_readl(W90X900PDID);
- if (idcode != W90P910_CPUID)
- printk(KERN_ERR "CPU type 0x%08lx is not W90P910\n", idcode);
-}
-
-/*Init W90P910 clock*/
-
-void __init w90p910_init_clocks(void)
-{
- clks_register(w90p910_clkregs, ARRAY_SIZE(w90p910_clkregs));
-}
-
-static int __init w90p910_init_cpu(void)
-{
- return 0;
-}
-
-static int __init w90x900_arch_init(void)
-{
- return w90p910_init_cpu();
-}
-arch_initcall(w90x900_arch_init);
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index 83c025e72ce..5fe595aeba6 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -758,7 +758,7 @@ config CACHE_FEROCEON_L2_WRITETHROUGH
config CACHE_L2X0
bool "Enable the L2x0 outer cache controller"
depends on REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176 || \
- REALVIEW_EB_A9MP || ARCH_MX35 || ARCH_MX31 || MACH_REALVIEW_PBX
+ REALVIEW_EB_A9MP || ARCH_MX35 || ARCH_MX31 || MACH_REALVIEW_PBX || ARCH_NOMADIK
default y
select OUTER_CACHE
help
diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index 03cd27d917b..b270d6228fe 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -159,7 +159,9 @@ union offset_union {
#define __get8_unaligned_check(ins,val,addr,err) \
__asm__( \
- "1: "ins" %1, [%2], #1\n" \
+ ARM( "1: "ins" %1, [%2], #1\n" ) \
+ THUMB( "1: "ins" %1, [%2]\n" ) \
+ THUMB( " add %2, %2, #1\n" ) \
"2:\n" \
" .section .fixup,\"ax\"\n" \
" .align 2\n" \
@@ -215,7 +217,9 @@ union offset_union {
do { \
unsigned int err = 0, v = val, a = addr; \
__asm__( FIRST_BYTE_16 \
- "1: "ins" %1, [%2], #1\n" \
+ ARM( "1: "ins" %1, [%2], #1\n" ) \
+ THUMB( "1: "ins" %1, [%2]\n" ) \
+ THUMB( " add %2, %2, #1\n" ) \
" mov %1, %1, "NEXT_BYTE"\n" \
"2: "ins" %1, [%2]\n" \
"3:\n" \
@@ -245,11 +249,17 @@ union offset_union {
do { \
unsigned int err = 0, v = val, a = addr; \
__asm__( FIRST_BYTE_32 \
- "1: "ins" %1, [%2], #1\n" \
+ ARM( "1: "ins" %1, [%2], #1\n" ) \
+ THUMB( "1: "ins" %1, [%2]\n" ) \
+ THUMB( " add %2, %2, #1\n" ) \
" mov %1, %1, "NEXT_BYTE"\n" \
- "2: "ins" %1, [%2], #1\n" \
+ ARM( "2: "ins" %1, [%2], #1\n" ) \
+ THUMB( "2: "ins" %1, [%2]\n" ) \
+ THUMB( " add %2, %2, #1\n" ) \
" mov %1, %1, "NEXT_BYTE"\n" \
- "3: "ins" %1, [%2], #1\n" \
+ ARM( "3: "ins" %1, [%2], #1\n" ) \
+ THUMB( "3: "ins" %1, [%2]\n" ) \
+ THUMB( " add %2, %2, #1\n" ) \
" mov %1, %1, "NEXT_BYTE"\n" \
"4: "ins" %1, [%2]\n" \
"5:\n" \
diff --git a/arch/arm/mm/cache-v7.S b/arch/arm/mm/cache-v7.S
index be93ff02a98..bda0ec31a4e 100644
--- a/arch/arm/mm/cache-v7.S
+++ b/arch/arm/mm/cache-v7.S
@@ -21,7 +21,7 @@
*
* Flush the whole D-cache.
*
- * Corrupted registers: r0-r5, r7, r9-r11
+ * Corrupted registers: r0-r7, r9-r11 (r6 only in Thumb mode)
*
* - mm - mm_struct describing address space
*/
@@ -51,8 +51,12 @@ loop1:
loop2:
mov r9, r4 @ create working copy of max way size
loop3:
- orr r11, r10, r9, lsl r5 @ factor way and cache number into r11
- orr r11, r11, r7, lsl r2 @ factor index number into r11
+ ARM( orr r11, r10, r9, lsl r5 ) @ factor way and cache number into r11
+ THUMB( lsl r6, r9, r5 )
+ THUMB( orr r11, r10, r6 ) @ factor way and cache number into r11
+ ARM( orr r11, r11, r7, lsl r2 ) @ factor index number into r11
+ THUMB( lsl r6, r7, r2 )
+ THUMB( orr r11, r11, r6 ) @ factor index number into r11
mcr p15, 0, r11, c7, c14, 2 @ clean & invalidate by set/way
subs r9, r9, #1 @ decrement the way
bge loop3
@@ -82,11 +86,13 @@ ENDPROC(v7_flush_dcache_all)
*
*/
ENTRY(v7_flush_kern_cache_all)
- stmfd sp!, {r4-r5, r7, r9-r11, lr}
+ ARM( stmfd sp!, {r4-r5, r7, r9-r11, lr} )
+ THUMB( stmfd sp!, {r4-r7, r9-r11, lr} )
bl v7_flush_dcache_all
mov r0, #0
mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate
- ldmfd sp!, {r4-r5, r7, r9-r11, lr}
+ ARM( ldmfd sp!, {r4-r5, r7, r9-r11, lr} )
+ THUMB( ldmfd sp!, {r4-r7, r9-r11, lr} )
mov pc, lr
ENDPROC(v7_flush_kern_cache_all)
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 510c179b0ac..b30925fcbcd 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -36,7 +36,34 @@
#define CONSISTENT_PTE_INDEX(x) (((unsigned long)(x) - CONSISTENT_BASE) >> PGDIR_SHIFT)
#define NUM_CONSISTENT_PTES (CONSISTENT_DMA_SIZE >> PGDIR_SHIFT)
+static u64 get_coherent_dma_mask(struct device *dev)
+{
+ u64 mask = ISA_DMA_THRESHOLD;
+
+ if (dev) {
+ mask = dev->coherent_dma_mask;
+
+ /*
+ * Sanity check the DMA mask - it must be non-zero, and
+ * must be able to be satisfied by a DMA allocation.
+ */
+ if (mask == 0) {
+ dev_warn(dev, "coherent DMA mask is unset\n");
+ return 0;
+ }
+
+ if ((~mask) & ISA_DMA_THRESHOLD) {
+ dev_warn(dev, "coherent DMA mask %#llx is smaller "
+ "than system GFP_DMA mask %#llx\n",
+ mask, (unsigned long long)ISA_DMA_THRESHOLD);
+ return 0;
+ }
+ }
+ return mask;
+}
+
+#ifdef CONFIG_MMU
/*
* These are the page tables (2MB each) covering uncached, DMA consistent allocations
*/
@@ -152,7 +179,8 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
struct page *page;
struct arm_vm_region *c;
unsigned long order;
- u64 mask = ISA_DMA_THRESHOLD, limit;
+ u64 mask = get_coherent_dma_mask(dev);
+ u64 limit;
if (!consistent_pte[0]) {
printk(KERN_ERR "%s: not initialised\n", __func__);
@@ -160,25 +188,8 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
return NULL;
}
- if (dev) {
- mask = dev->coherent_dma_mask;
-
- /*
- * Sanity check the DMA mask - it must be non-zero, and
- * must be able to be satisfied by a DMA allocation.
- */
- if (mask == 0) {
- dev_warn(dev, "coherent DMA mask is unset\n");
- goto no_page;
- }
-
- if ((~mask) & ISA_DMA_THRESHOLD) {
- dev_warn(dev, "coherent DMA mask %#llx is smaller "
- "than system GFP_DMA mask %#llx\n",
- mask, (unsigned long long)ISA_DMA_THRESHOLD);
- goto no_page;
- }
- }
+ if (!mask)
+ goto no_page;
/*
* Sanity check the allocation size.
@@ -267,6 +278,31 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
*handle = ~0;
return NULL;
}
+#else /* !CONFIG_MMU */
+static void *
+__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp,
+ pgprot_t prot)
+{
+ void *virt;
+ u64 mask = get_coherent_dma_mask(dev);
+
+ if (!mask)
+ goto error;
+
+ if (mask != 0xffffffff)
+ gfp |= GFP_DMA;
+ virt = kmalloc(size, gfp);
+ if (!virt)
+ goto error;
+
+ *handle = virt_to_dma(dev, virt);
+ return virt;
+
+error:
+ *handle = ~0;
+ return NULL;
+}
+#endif /* CONFIG_MMU */
/*
* Allocate DMA-coherent memory space and return both the kernel remapped
@@ -311,9 +347,10 @@ EXPORT_SYMBOL(dma_alloc_writecombine);
static int dma_mmap(struct device *dev, struct vm_area_struct *vma,
void *cpu_addr, dma_addr_t dma_addr, size_t size)
{
+ int ret = -ENXIO;
+#ifdef CONFIG_MMU
unsigned long flags, user_size, kern_size;
struct arm_vm_region *c;
- int ret = -ENXIO;
user_size = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
@@ -334,6 +371,7 @@ static int dma_mmap(struct device *dev, struct vm_area_struct *vma,
vma->vm_page_prot);
}
}
+#endif /* CONFIG_MMU */
return ret;
}
@@ -358,6 +396,7 @@ EXPORT_SYMBOL(dma_mmap_writecombine);
* free a page as defined by the above mapping.
* Must not be called with IRQs disabled.
*/
+#ifdef CONFIG_MMU
void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle)
{
struct arm_vm_region *c;
@@ -444,6 +483,14 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr
__func__, cpu_addr);
dump_stack();
}
+#else /* !CONFIG_MMU */
+void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle)
+{
+ if (dma_release_from_coherent(dev, get_order(size), cpu_addr))
+ return;
+ kfree(cpu_addr);
+}
+#endif /* CONFIG_MMU */
EXPORT_SYMBOL(dma_free_coherent);
/*
@@ -451,10 +498,12 @@ EXPORT_SYMBOL(dma_free_coherent);
*/
static int __init consistent_init(void)
{
+ int ret = 0;
+#ifdef CONFIG_MMU
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
- int ret = 0, i = 0;
+ int i = 0;
u32 base = CONSISTENT_BASE;
do {
@@ -477,6 +526,7 @@ static int __init consistent_init(void)
consistent_pte[i++] = pte;
base += (1 << PGDIR_SHIFT);
} while (base < CONSISTENT_END);
+#endif /* !CONFIG_MMU */
return ret;
}
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index 6fdcbb70982..cc8829d7e11 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -16,6 +16,8 @@
#include <linux/kprobes.h>
#include <linux/uaccess.h>
#include <linux/page-flags.h>
+#include <linux/sched.h>
+#include <linux/highmem.h>
#include <asm/system.h>
#include <asm/pgtable.h>
@@ -23,6 +25,7 @@
#include "fault.h"
+#ifdef CONFIG_MMU
#ifdef CONFIG_KPROBES
static inline int notify_page_fault(struct pt_regs *regs, unsigned int fsr)
@@ -97,6 +100,10 @@ void show_pte(struct mm_struct *mm, unsigned long addr)
printk("\n");
}
+#else /* CONFIG_MMU */
+void show_pte(struct mm_struct *mm, unsigned long addr)
+{ }
+#endif /* CONFIG_MMU */
/*
* Oops. The kernel tried to access some page that wasn't present.
@@ -171,6 +178,7 @@ void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
__do_kernel_fault(mm, addr, fsr, regs);
}
+#ifdef CONFIG_MMU
#define VM_FAULT_BADMAP 0x010000
#define VM_FAULT_BADACCESS 0x020000
@@ -322,6 +330,13 @@ no_context:
__do_kernel_fault(mm, addr, fsr, regs);
return 0;
}
+#else /* CONFIG_MMU */
+static int
+do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
+{
+ return 0;
+}
+#endif /* CONFIG_MMU */
/*
* First Level Translation Fault Handler
@@ -340,6 +355,7 @@ no_context:
* interrupt or a critical region, and should only copy the information
* from the master page table, nothing more.
*/
+#ifdef CONFIG_MMU
static int __kprobes
do_translation_fault(unsigned long addr, unsigned int fsr,
struct pt_regs *regs)
@@ -378,6 +394,14 @@ bad_area:
do_bad_area(addr, fsr, regs);
return 0;
}
+#else /* CONFIG_MMU */
+static int
+do_translation_fault(unsigned long addr, unsigned int fsr,
+ struct pt_regs *regs)
+{
+ return 0;
+}
+#endif /* CONFIG_MMU */
/*
* Some section permission faults need to be handled gracefully.
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index c07222eb5ce..575f3ad722e 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -144,7 +144,14 @@ void __flush_dcache_page(struct address_space *mapping, struct page *page)
* page. This ensures that data in the physical page is mutually
* coherent with the kernels mapping.
*/
- __cpuc_flush_dcache_page(page_address(page));
+#ifdef CONFIG_HIGHMEM
+ /*
+ * kmap_atomic() doesn't set the page virtual address, and
+ * kunmap_atomic() takes care of cache flushing already.
+ */
+ if (page_address(page))
+#endif
+ __cpuc_flush_dcache_page(page_address(page));
/*
* If this is a page cache page, and we have an aliasing VIPT cache,
diff --git a/arch/arm/mm/highmem.c b/arch/arm/mm/highmem.c
index a34954d9df7..73cae57fa70 100644
--- a/arch/arm/mm/highmem.c
+++ b/arch/arm/mm/highmem.c
@@ -40,11 +40,16 @@ void *kmap_atomic(struct page *page, enum km_type type)
{
unsigned int idx;
unsigned long vaddr;
+ void *kmap;
pagefault_disable();
if (!PageHighMem(page))
return page_address(page);
+ kmap = kmap_high_get(page);
+ if (kmap)
+ return kmap;
+
idx = type + KM_TYPE_NR * smp_processor_id();
vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
#ifdef CONFIG_DEBUG_HIGHMEM
@@ -80,6 +85,9 @@ void kunmap_atomic(void *kvaddr, enum km_type type)
#else
(void) idx; /* to kill a warning */
#endif
+ } else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
+ /* this address was obtained through kmap_high_get() */
+ kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)]));
}
pagefault_enable();
}
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index 3a7279c1ce5..ea36186f32c 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -15,6 +15,7 @@
#include <linux/mman.h>
#include <linux/nodemask.h>
#include <linux/initrd.h>
+#include <linux/sort.h>
#include <linux/highmem.h>
#include <asm/mach-types.h>
@@ -349,12 +350,43 @@ static void __init bootmem_free_node(int node, struct meminfo *mi)
free_area_init_node(node, zone_size, min, zhole_size);
}
+#ifndef CONFIG_SPARSEMEM
+int pfn_valid(unsigned long pfn)
+{
+ struct meminfo *mi = &meminfo;
+ unsigned int left = 0, right = mi->nr_banks;
+
+ do {
+ unsigned int mid = (right + left) / 2;
+ struct membank *bank = &mi->bank[mid];
+
+ if (pfn < bank_pfn_start(bank))
+ right = mid;
+ else if (pfn >= bank_pfn_end(bank))
+ left = mid + 1;
+ else
+ return 1;
+ } while (left < right);
+ return 0;
+}
+EXPORT_SYMBOL(pfn_valid);
+#endif
+
+static int __init meminfo_cmp(const void *_a, const void *_b)
+{
+ const struct membank *a = _a, *b = _b;
+ long cmp = bank_pfn_start(a) - bank_pfn_start(b);
+ return cmp < 0 ? -1 : cmp > 0 ? 1 : 0;
+}
+
void __init bootmem_init(void)
{
struct meminfo *mi = &meminfo;
unsigned long min, max_low, max_high;
int node, initrd_node;
+ sort(&mi->bank, mi->nr_banks, sizeof(mi->bank[0]), meminfo_cmp, NULL);
+
/*
* Locate which node contains the ramdisk image, if any.
*/
diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c
index ad7bacc693b..900811cc913 100644
--- a/arch/arm/mm/nommu.c
+++ b/arch/arm/mm/nommu.c
@@ -12,6 +12,7 @@
#include <asm/cacheflush.h>
#include <asm/sections.h>
#include <asm/page.h>
+#include <asm/setup.h>
#include <asm/mach/arch.h>
#include "mm.h"
diff --git a/arch/arm/mm/proc-macros.S b/arch/arm/mm/proc-macros.S
index 54b1f721dec..7d63beaf974 100644
--- a/arch/arm/mm/proc-macros.S
+++ b/arch/arm/mm/proc-macros.S
@@ -77,19 +77,15 @@
* Sanity check the PTE configuration for the code below - which makes
* certain assumptions about how these bits are layed out.
*/
+#ifdef CONFIG_MMU
#if L_PTE_SHARED != PTE_EXT_SHARED
#error PTE shared bit mismatch
#endif
-#if L_PTE_BUFFERABLE != PTE_BUFFERABLE
-#error PTE bufferable bit mismatch
-#endif
-#if L_PTE_CACHEABLE != PTE_CACHEABLE
-#error PTE cacheable bit mismatch
-#endif
#if (L_PTE_EXEC+L_PTE_USER+L_PTE_WRITE+L_PTE_DIRTY+L_PTE_YOUNG+\
L_PTE_FILE+L_PTE_PRESENT) > L_PTE_SHARED
#error Invalid Linux PTE bit settings
#endif
+#endif /* CONFIG_MMU */
/*
* The ARMv6 and ARMv7 set_pte_ext translation function.
diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S
index 180a08d03a0..f3fa1c32fe9 100644
--- a/arch/arm/mm/proc-v7.S
+++ b/arch/arm/mm/proc-v7.S
@@ -127,7 +127,9 @@ ENDPROC(cpu_v7_switch_mm)
*/
ENTRY(cpu_v7_set_pte_ext)
#ifdef CONFIG_MMU
- str r1, [r0], #-2048 @ linux version
+ ARM( str r1, [r0], #-2048 ) @ linux version
+ THUMB( str r1, [r0] ) @ linux version
+ THUMB( sub r0, r0, #2048 )
bic r3, r1, #0x000003f0
bic r3, r3, #PTE_TYPE_MASK
@@ -232,7 +234,6 @@ __v7_setup:
mcr p15, 0, r4, c2, c0, 1 @ load TTB1
mov r10, #0x1f @ domains 0, 1 = manager
mcr p15, 0, r10, c3, c0, 0 @ load domain access register
-#endif
/*
* Memory region attributes with SCTLR.TRE=1
*
@@ -265,6 +266,7 @@ __v7_setup:
ldr r6, =0x40e040e0 @ NMRR
mcr p15, 0, r5, c10, c2, 0 @ write PRRR
mcr p15, 0, r6, c10, c2, 1 @ write NMRR
+#endif
adr r5, v7_crval
ldmia r5, {r5, r6}
#ifdef CONFIG_CPU_ENDIAN_BE8
@@ -273,6 +275,7 @@ __v7_setup:
mrc p15, 0, r0, c1, c0, 0 @ read control register
bic r0, r0, r5 @ clear bits them
orr r0, r0, r6 @ set them
+ THUMB( orr r0, r0, #1 << 30 ) @ Thumb exceptions
mov pc, lr @ return to head.S:__ret
ENDPROC(__v7_setup)
diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig
index 8986b741223..ca5c7c22634 100644
--- a/arch/arm/plat-mxc/Kconfig
+++ b/arch/arm/plat-mxc/Kconfig
@@ -9,6 +9,7 @@ choice
config ARCH_MX1
bool "MX1-based"
select CPU_ARM920T
+ select COMMON_CLKDEV
help
This enables support for systems based on the Freescale i.MX1 family
@@ -19,6 +20,13 @@ config ARCH_MX2
help
This enables support for systems based on the Freescale i.MX2 family
+config ARCH_MX25
+ bool "MX25-based"
+ select CPU_ARM926T
+ select COMMON_CLKDEV
+ help
+ This enables support for systems based on the Freescale i.MX25 family
+
config ARCH_MX3
bool "MX3-based"
select CPU_V6
@@ -26,11 +34,20 @@ config ARCH_MX3
help
This enables support for systems based on the Freescale i.MX3 family
+config ARCH_MXC91231
+ bool "MXC91231-based"
+ select CPU_V6
+ select COMMON_CLKDEV
+ help
+ This enables support for systems based on the Freescale MXC91231 family
+
endchoice
source "arch/arm/mach-mx1/Kconfig"
source "arch/arm/mach-mx2/Kconfig"
source "arch/arm/mach-mx3/Kconfig"
+source "arch/arm/mach-mx25/Kconfig"
+source "arch/arm/mach-mxc91231/Kconfig"
endmenu
diff --git a/arch/arm/plat-mxc/clock.c b/arch/arm/plat-mxc/clock.c
index 92e13566cd4..9e8fbd57495 100644
--- a/arch/arm/plat-mxc/clock.c
+++ b/arch/arm/plat-mxc/clock.c
@@ -39,6 +39,7 @@
#include <linux/string.h>
#include <mach/clock.h>
+#include <mach/hardware.h>
static LIST_HEAD(clocks);
static DEFINE_MUTEX(clocks_mutex);
@@ -47,76 +48,6 @@ static DEFINE_MUTEX(clocks_mutex);
* Standard clock functions defined in include/linux/clk.h
*-------------------------------------------------------------------------*/
-/*
- * All the code inside #ifndef CONFIG_COMMON_CLKDEV can be removed once all
- * MXC architectures have switched to using clkdev.
- */
-#ifndef CONFIG_COMMON_CLKDEV
-/*
- * Retrieve a clock by name.
- *
- * Note that we first try to use device id on the bus
- * and clock name. If this fails, we try to use "<name>.<id>". If this fails,
- * we try to use clock name only.
- * The reference count to the clock's module owner ref count is incremented.
- */
-struct clk *clk_get(struct device *dev, const char *id)
-{
- struct clk *p, *clk = ERR_PTR(-ENOENT);
- int idno;
- const char *str;
-
- if (id == NULL)
- return clk;
-
- if (dev == NULL || dev->bus != &platform_bus_type)
- idno = -1;
- else
- idno = to_platform_device(dev)->id;
-
- mutex_lock(&clocks_mutex);
-
- list_for_each_entry(p, &clocks, node) {
- if (p->id == idno &&
- strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
- clk = p;
- goto found;
- }
- }
-
- str = strrchr(id, '.');
- if (str) {
- int cnt = str - id;
- str++;
- idno = simple_strtol(str, NULL, 10);
- list_for_each_entry(p, &clocks, node) {
- if (p->id == idno &&
- strlen(p->name) == cnt &&
- strncmp(id, p->name, cnt) == 0 &&
- try_module_get(p->owner)) {
- clk = p;
- goto found;
- }
- }
- }
-
- list_for_each_entry(p, &clocks, node) {
- if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
- clk = p;
- goto found;
- }
- }
-
- printk(KERN_WARNING "clk: Unable to get requested clock: %s\n", id);
-
-found:
- mutex_unlock(&clocks_mutex);
-
- return clk;
-}
-EXPORT_SYMBOL(clk_get);
-#endif
-
static void __clk_disable(struct clk *clk)
{
if (clk == NULL || IS_ERR(clk))
@@ -193,16 +124,6 @@ unsigned long clk_get_rate(struct clk *clk)
}
EXPORT_SYMBOL(clk_get_rate);
-#ifndef CONFIG_COMMON_CLKDEV
-/* Decrement the clock's module reference count */
-void clk_put(struct clk *clk)
-{
- if (clk && !IS_ERR(clk))
- module_put(clk->owner);
-}
-EXPORT_SYMBOL(clk_put);
-#endif
-
/* Round the requested clock rate to the nearest supported
* rate that is less than or equal to the requested rate.
* This is dependent on the clock's current parent.
@@ -265,80 +186,6 @@ struct clk *clk_get_parent(struct clk *clk)
}
EXPORT_SYMBOL(clk_get_parent);
-#ifndef CONFIG_COMMON_CLKDEV
-/*
- * Add a new clock to the clock tree.
- */
-int clk_register(struct clk *clk)
-{
- if (clk == NULL || IS_ERR(clk))
- return -EINVAL;
-
- mutex_lock(&clocks_mutex);
- list_add(&clk->node, &clocks);
- mutex_unlock(&clocks_mutex);
-
- return 0;
-}
-EXPORT_SYMBOL(clk_register);
-
-/* Remove a clock from the clock tree */
-void clk_unregister(struct clk *clk)
-{
- if (clk == NULL || IS_ERR(clk))
- return;
-
- mutex_lock(&clocks_mutex);
- list_del(&clk->node);
- mutex_unlock(&clocks_mutex);
-}
-EXPORT_SYMBOL(clk_unregister);
-
-#ifdef CONFIG_PROC_FS
-static int mxc_clock_read_proc(char *page, char **start, off_t off,
- int count, int *eof, void *data)
-{
- struct clk *clkp;
- char *p = page;
- int len;
-
- list_for_each_entry(clkp, &clocks, node) {
- p += sprintf(p, "%s-%d:\t\t%lu, %d", clkp->name, clkp->id,
- clk_get_rate(clkp), clkp->usecount);
- if (clkp->parent)
- p += sprintf(p, ", %s-%d\n", clkp->parent->name,
- clkp->parent->id);
- else
- p += sprintf(p, "\n");
- }
-
- len = (p - page) - off;
- if (len < 0)
- len = 0;
-
- *eof = (len <= count) ? 1 : 0;
- *start = page + off;
-
- return len;
-}
-
-static int __init mxc_setup_proc_entry(void)
-{
- struct proc_dir_entry *res;
-
- res = create_proc_read_entry("cpu/clocks", 0, NULL,
- mxc_clock_read_proc, NULL);
- if (!res) {
- printk(KERN_ERR "Failed to create proc/cpu/clocks\n");
- return -ENOMEM;
- }
- return 0;
-}
-
-late_initcall(mxc_setup_proc_entry);
-#endif /* CONFIG_PROC_FS */
-#endif
-
/*
* Get the resulting clock rate from a PLL register value and the input
* frequency. PLLs with this register layout can at least be found on
@@ -363,12 +210,11 @@ unsigned long mxc_decode_pll(unsigned int reg_val, u32 freq)
mfn_abs = mfn;
-#if !defined CONFIG_ARCH_MX1 && !defined CONFIG_ARCH_MX21
- if (mfn >= 0x200) {
- mfn |= 0xFFFFFE00;
- mfn_abs = -mfn;
- }
-#endif
+ /* On all i.MXs except i.MX1 and i.MX21 mfn is a 10bit
+ * 2's complements number
+ */
+ if (!cpu_is_mx1() && !cpu_is_mx21() && mfn >= 0x200)
+ mfn_abs = 0x400 - mfn;
freq *= 2;
freq /= pd + 1;
@@ -376,8 +222,10 @@ unsigned long mxc_decode_pll(unsigned int reg_val, u32 freq)
ll = (unsigned long long)freq * mfn_abs;
do_div(ll, mfd + 1);
- if (mfn < 0)
+
+ if (!cpu_is_mx1() && !cpu_is_mx21() && mfn >= 0x200)
ll = -ll;
+
ll = (freq * mfi) + ll;
return ll;
diff --git a/arch/arm/plat-mxc/gpio.c b/arch/arm/plat-mxc/gpio.c
index 7506d963be4..cfc4a8b43e6 100644
--- a/arch/arm/plat-mxc/gpio.c
+++ b/arch/arm/plat-mxc/gpio.c
@@ -29,6 +29,23 @@
static struct mxc_gpio_port *mxc_gpio_ports;
static int gpio_table_size;
+#define cpu_is_mx1_mx2() (cpu_is_mx1() || cpu_is_mx2())
+
+#define GPIO_DR (cpu_is_mx1_mx2() ? 0x1c : 0x00)
+#define GPIO_GDIR (cpu_is_mx1_mx2() ? 0x00 : 0x04)
+#define GPIO_PSR (cpu_is_mx1_mx2() ? 0x24 : 0x08)
+#define GPIO_ICR1 (cpu_is_mx1_mx2() ? 0x28 : 0x0C)
+#define GPIO_ICR2 (cpu_is_mx1_mx2() ? 0x2C : 0x10)
+#define GPIO_IMR (cpu_is_mx1_mx2() ? 0x30 : 0x14)
+#define GPIO_ISR (cpu_is_mx1_mx2() ? 0x34 : 0x18)
+#define GPIO_ISR (cpu_is_mx1_mx2() ? 0x34 : 0x18)
+
+#define GPIO_INT_LOW_LEV (cpu_is_mx1_mx2() ? 0x3 : 0x0)
+#define GPIO_INT_HIGH_LEV (cpu_is_mx1_mx2() ? 0x2 : 0x1)
+#define GPIO_INT_RISE_EDGE (cpu_is_mx1_mx2() ? 0x0 : 0x2)
+#define GPIO_INT_FALL_EDGE (cpu_is_mx1_mx2() ? 0x1 : 0x3)
+#define GPIO_INT_NONE 0x4
+
/* Note: This driver assumes 32 GPIOs are handled in one register */
static void _clear_gpio_irqstatus(struct mxc_gpio_port *port, u32 index)
@@ -162,7 +179,6 @@ static void mxc_gpio_irq_handler(struct mxc_gpio_port *port, u32 irq_stat)
}
}
-#if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX1)
/* MX1 and MX3 has one interrupt *per* gpio port */
static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
{
@@ -174,9 +190,7 @@ static void mx3_gpio_irq_handler(u32 irq, struct irq_desc *desc)
mxc_gpio_irq_handler(port, irq_stat);
}
-#endif
-#ifdef CONFIG_ARCH_MX2
/* MX2 has one interrupt *for all* gpio ports */
static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc)
{
@@ -195,7 +209,6 @@ static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc)
mxc_gpio_irq_handler(&port[i], irq_stat);
}
}
-#endif
static struct irq_chip gpio_irq_chip = {
.ack = gpio_ack_irq,
@@ -284,17 +297,18 @@ int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt)
/* its a serious configuration bug when it fails */
BUG_ON( gpiochip_add(&port[i].chip) < 0 );
-#if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX1)
- /* setup one handler for each entry */
- set_irq_chained_handler(port[i].irq, mx3_gpio_irq_handler);
- set_irq_data(port[i].irq, &port[i]);
-#endif
+ if (cpu_is_mx1() || cpu_is_mx3() || cpu_is_mx25()) {
+ /* setup one handler for each entry */
+ set_irq_chained_handler(port[i].irq, mx3_gpio_irq_handler);
+ set_irq_data(port[i].irq, &port[i]);
+ }
+ }
+
+ if (cpu_is_mx2()) {
+ /* setup one handler for all GPIO interrupts */
+ set_irq_chained_handler(port[0].irq, mx2_gpio_irq_handler);
+ set_irq_data(port[0].irq, port);
}
-#ifdef CONFIG_ARCH_MX2
- /* setup one handler for all GPIO interrupts */
- set_irq_chained_handler(port[0].irq, mx2_gpio_irq_handler);
- set_irq_data(port[0].irq, port);
-#endif
return 0;
}
diff --git a/arch/arm/plat-mxc/include/mach/board-armadillo5x0.h b/arch/arm/plat-mxc/include/mach/board-armadillo5x0.h
index 8769e910e55..0376c133c9f 100644
--- a/arch/arm/plat-mxc/include/mach/board-armadillo5x0.h
+++ b/arch/arm/plat-mxc/include/mach/board-armadillo5x0.h
@@ -12,11 +12,4 @@
#ifndef __ASM_ARCH_MXC_BOARD_ARMADILLO5X0_H__
#define __ASM_ARCH_MXC_BOARD_ARMADILLO5X0_H__
-#include <mach/hardware.h>
-
-/* mandatory for CONFIG_DEBUG_LL */
-
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
-
#endif
diff --git a/arch/arm/plat-mxc/include/mach/board-eukrea_cpuimx27.h b/arch/arm/plat-mxc/include/mach/board-eukrea_cpuimx27.h
new file mode 100644
index 00000000000..a1fd5830af4
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/board-eukrea_cpuimx27.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 Eric Benard - eric@eukrea.com
+ *
+ * Based on board-pcm038.h which is :
+ * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ */
+
+#ifndef __ASM_ARCH_MXC_BOARD_EUKREA_CPUIMX27_H__
+#define __ASM_ARCH_MXC_BOARD_EUKREA_CPUIMX27_H__
+
+#ifndef __ASSEMBLY__
+/*
+ * This CPU module needs a baseboard to work. After basic initializing
+ * its own devices, it calls baseboard's init function.
+ * TODO: Add your own baseboard init function and call it from
+ * inside eukrea_cpuimx27_init().
+ *
+ * This example here is for the development board. Refer
+ * eukrea_mbimx27-baseboard.c
+ */
+
+extern void eukrea_mbimx27_baseboard_init(void);
+
+#endif
+
+#endif /* __ASM_ARCH_MXC_BOARD_EUKREA_CPUIMX27_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/board-mx21ads.h b/arch/arm/plat-mxc/include/mach/board-mx21ads.h
index 06701df74c4..0cf4fa29510 100644
--- a/arch/arm/plat-mxc/include/mach/board-mx21ads.h
+++ b/arch/arm/plat-mxc/include/mach/board-mx21ads.h
@@ -15,12 +15,6 @@
#define __ASM_ARCH_MXC_BOARD_MX21ADS_H__
/*
- * MXC UART EVB board level configurations
- */
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR AIPI_IO_ADDRESS(UART1_BASE_ADDR)
-
-/*
* Memory-mapped I/O on MX21ADS base board
*/
#define MX21ADS_MMIO_BASE_ADDR 0xF5000000
diff --git a/arch/arm/plat-mxc/include/mach/board-mx27ads.h b/arch/arm/plat-mxc/include/mach/board-mx27ads.h
index d42f4e6116f..7776d230327 100644
--- a/arch/arm/plat-mxc/include/mach/board-mx27ads.h
+++ b/arch/arm/plat-mxc/include/mach/board-mx27ads.h
@@ -26,12 +26,6 @@
MXC_MAX_VIRTUAL_INTS)
/*
- * MXC UART EVB board level configurations
- */
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR AIPI_IO_ADDRESS(UART1_BASE_ADDR)
-
-/*
* @name Memory Size parameters
*/
diff --git a/arch/arm/plat-mxc/include/mach/board-mx27lite.h b/arch/arm/plat-mxc/include/mach/board-mx27lite.h
index a870f8ea244..ea87551d273 100644
--- a/arch/arm/plat-mxc/include/mach/board-mx27lite.h
+++ b/arch/arm/plat-mxc/include/mach/board-mx27lite.h
@@ -11,9 +11,4 @@
#ifndef __ASM_ARCH_MXC_BOARD_MX27LITE_H__
#define __ASM_ARCH_MXC_BOARD_MX27LITE_H__
-/* mandatory for CONFIG_DEBUG_LL */
-
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
-
#endif /* __ASM_ARCH_MXC_BOARD_MX27LITE_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/board-mx27pdk.h b/arch/arm/plat-mxc/include/mach/board-mx27pdk.h
index 552b55d714d..fec1bcfa916 100644
--- a/arch/arm/plat-mxc/include/mach/board-mx27pdk.h
+++ b/arch/arm/plat-mxc/include/mach/board-mx27pdk.h
@@ -11,9 +11,4 @@
#ifndef __ASM_ARCH_MXC_BOARD_MX27PDK_H__
#define __ASM_ARCH_MXC_BOARD_MX27PDK_H__
-/* mandatory for CONFIG_DEBUG_LL */
-
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
-
#endif /* __ASM_ARCH_MXC_BOARD_MX27PDK_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/board-mx31ads.h b/arch/arm/plat-mxc/include/mach/board-mx31ads.h
index 06e6895f7f6..2cbfa35e82f 100644
--- a/arch/arm/plat-mxc/include/mach/board-mx31ads.h
+++ b/arch/arm/plat-mxc/include/mach/board-mx31ads.h
@@ -114,9 +114,4 @@
#define MXC_MAX_EXP_IO_LINES 16
-/* mandatory for CONFIG_DEBUG_LL */
-
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
-
#endif /* __ASM_ARCH_MXC_BOARD_MX31ADS_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/board-mx31lilly.h b/arch/arm/plat-mxc/include/mach/board-mx31lilly.h
index 78cf31e22e4..eb5a5024622 100644
--- a/arch/arm/plat-mxc/include/mach/board-mx31lilly.h
+++ b/arch/arm/plat-mxc/include/mach/board-mx31lilly.h
@@ -22,11 +22,6 @@
#ifndef __ASM_ARCH_MXC_BOARD_MX31LILLY_H__
#define __ASM_ARCH_MXC_BOARD_MX31LILLY_H__
-/* mandatory for CONFIG_LL_DEBUG */
-
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR (AIPI_BASE_ADDR_VIRT + 0x0A000)
-
#ifndef __ASSEMBLY__
enum mx31lilly_boards {
diff --git a/arch/arm/plat-mxc/include/mach/board-mx31lite.h b/arch/arm/plat-mxc/include/mach/board-mx31lite.h
index 52fbdf2d6f2..8e64325d690 100644
--- a/arch/arm/plat-mxc/include/mach/board-mx31lite.h
+++ b/arch/arm/plat-mxc/include/mach/board-mx31lite.h
@@ -11,8 +11,5 @@
#ifndef __ASM_ARCH_MXC_BOARD_MX31LITE_H__
#define __ASM_ARCH_MXC_BOARD_MX31LITE_H__
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
-
#endif /* __ASM_ARCH_MXC_BOARD_MX31LITE_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/board-mx31moboard.h b/arch/arm/plat-mxc/include/mach/board-mx31moboard.h
index 303fd2434a2..d5be6b5a6ac 100644
--- a/arch/arm/plat-mxc/include/mach/board-mx31moboard.h
+++ b/arch/arm/plat-mxc/include/mach/board-mx31moboard.h
@@ -19,11 +19,6 @@
#ifndef __ASM_ARCH_MXC_BOARD_MX31MOBOARD_H__
#define __ASM_ARCH_MXC_BOARD_MX31MOBOARD_H__
-/* mandatory for CONFIG_DEBUG_LL */
-
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR (AIPI_BASE_ADDR_VIRT + 0x0A000)
-
#ifndef __ASSEMBLY__
enum mx31moboard_boards {
diff --git a/arch/arm/plat-mxc/include/mach/board-mx31pdk.h b/arch/arm/plat-mxc/include/mach/board-mx31pdk.h
index 519bab3eb28..2bbd6ed17f5 100644
--- a/arch/arm/plat-mxc/include/mach/board-mx31pdk.h
+++ b/arch/arm/plat-mxc/include/mach/board-mx31pdk.h
@@ -11,11 +11,6 @@
#ifndef __ASM_ARCH_MXC_BOARD_MX31PDK_H__
#define __ASM_ARCH_MXC_BOARD_MX31PDK_H__
-/* mandatory for CONFIG_DEBUG_LL */
-
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
-
/* Definitions for components on the Debug board */
/* Base address of CPLD controller on the Debug board */
diff --git a/arch/arm/plat-mxc/include/mach/board-mx35pdk.h b/arch/arm/plat-mxc/include/mach/board-mx35pdk.h
index 1111037d6d9..383f1c04df0 100644
--- a/arch/arm/plat-mxc/include/mach/board-mx35pdk.h
+++ b/arch/arm/plat-mxc/include/mach/board-mx35pdk.h
@@ -19,9 +19,4 @@
#ifndef __ASM_ARCH_MXC_BOARD_MX35PDK_H__
#define __ASM_ARCH_MXC_BOARD_MX35PDK_H__
-/* mandatory for CONFIG_DEBUG_LL */
-
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
-
#endif /* __ASM_ARCH_MXC_BOARD_MX35PDK_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/board-pcm037.h b/arch/arm/plat-mxc/include/mach/board-pcm037.h
index f0a1fa1938a..13411709b13 100644
--- a/arch/arm/plat-mxc/include/mach/board-pcm037.h
+++ b/arch/arm/plat-mxc/include/mach/board-pcm037.h
@@ -19,9 +19,4 @@
#ifndef __ASM_ARCH_MXC_BOARD_PCM037_H__
#define __ASM_ARCH_MXC_BOARD_PCM037_H__
-/* mandatory for CONFIG_DEBUG_LL */
-
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
-
#endif /* __ASM_ARCH_MXC_BOARD_PCM037_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/board-pcm038.h b/arch/arm/plat-mxc/include/mach/board-pcm038.h
index 4fcd7499e09..410f9786ed2 100644
--- a/arch/arm/plat-mxc/include/mach/board-pcm038.h
+++ b/arch/arm/plat-mxc/include/mach/board-pcm038.h
@@ -19,11 +19,6 @@
#ifndef __ASM_ARCH_MXC_BOARD_PCM038_H__
#define __ASM_ARCH_MXC_BOARD_PCM038_H__
-/* mandatory for CONFIG_DEBUG_LL */
-
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR (AIPI_BASE_ADDR_VIRT + 0x0A000)
-
#ifndef __ASSEMBLY__
/*
* This CPU module needs a baseboard to work. After basic initializing
diff --git a/arch/arm/plat-mxc/include/mach/board-pcm043.h b/arch/arm/plat-mxc/include/mach/board-pcm043.h
index 15fbdf16abc..1ac4e1682e5 100644
--- a/arch/arm/plat-mxc/include/mach/board-pcm043.h
+++ b/arch/arm/plat-mxc/include/mach/board-pcm043.h
@@ -19,9 +19,4 @@
#ifndef __ASM_ARCH_MXC_BOARD_PCM043_H__
#define __ASM_ARCH_MXC_BOARD_PCM043_H__
-/* mandatory for CONFIG_LL_DEBUG */
-
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
-
#endif /* __ASM_ARCH_MXC_BOARD_PCM043_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/board-qong.h b/arch/arm/plat-mxc/include/mach/board-qong.h
index 04033ec637d..6d88c7af4b2 100644
--- a/arch/arm/plat-mxc/include/mach/board-qong.h
+++ b/arch/arm/plat-mxc/include/mach/board-qong.h
@@ -11,11 +11,6 @@
#ifndef __ASM_ARCH_MXC_BOARD_QONG_H__
#define __ASM_ARCH_MXC_BOARD_QONG_H__
-/* mandatory for CONFIG_DEBUG_LL */
-
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
-
/* NOR FLASH */
#define QONG_NOR_SIZE (128*1024*1024)
diff --git a/arch/arm/plat-mxc/include/mach/common.h b/arch/arm/plat-mxc/include/mach/common.h
index 02c3cd004db..286cb9b0a25 100644
--- a/arch/arm/plat-mxc/include/mach/common.h
+++ b/arch/arm/plat-mxc/include/mach/common.h
@@ -16,18 +16,33 @@ struct clk;
extern void mx1_map_io(void);
extern void mx21_map_io(void);
+extern void mx25_map_io(void);
extern void mx27_map_io(void);
extern void mx31_map_io(void);
extern void mx35_map_io(void);
-extern void mxc_init_irq(void);
-extern void mxc_timer_init(struct clk *timer_clk);
+extern void mxc91231_map_io(void);
+extern void mxc_init_irq(void __iomem *);
+extern void mx1_init_irq(void);
+extern void mx21_init_irq(void);
+extern void mx25_init_irq(void);
+extern void mx27_init_irq(void);
+extern void mx31_init_irq(void);
+extern void mx35_init_irq(void);
+extern void mxc91231_init_irq(void);
+extern void mxc_timer_init(struct clk *timer_clk, void __iomem *, int);
extern int mx1_clocks_init(unsigned long fref);
extern int mx21_clocks_init(unsigned long lref, unsigned long fref);
+extern int mx25_clocks_init(unsigned long fref);
extern int mx27_clocks_init(unsigned long fref);
extern int mx31_clocks_init(unsigned long fref);
extern int mx35_clocks_init(void);
+extern int mxc91231_clocks_init(unsigned long fref);
extern int mxc_register_gpios(void);
extern int mxc_register_device(struct platform_device *pdev, void *data);
extern void mxc_set_cpu_type(unsigned int type);
+extern void mxc_arch_reset_init(void __iomem *);
+extern void mxc91231_power_off(void);
+extern void mxc91231_arch_reset(int, const char *);
+extern void mxc91231_prepare_idle(void);
#endif
diff --git a/arch/arm/plat-mxc/include/mach/debug-macro.S b/arch/arm/plat-mxc/include/mach/debug-macro.S
index bbc5f6753cf..15b2b148a10 100644
--- a/arch/arm/plat-mxc/include/mach/debug-macro.S
+++ b/arch/arm/plat-mxc/include/mach/debug-macro.S
@@ -11,52 +11,52 @@
*
*/
-#include <mach/hardware.h>
-
-#ifdef CONFIG_MACH_MX31ADS
-#include <mach/board-mx31ads.h>
-#endif
-#ifdef CONFIG_MACH_PCM037
-#include <mach/board-pcm037.h>
-#endif
-#ifdef CONFIG_MACH_MX31LITE
-#include <mach/board-mx31lite.h>
-#endif
-#ifdef CONFIG_MACH_MX27ADS
-#include <mach/board-mx27ads.h>
-#endif
-#ifdef CONFIG_MACH_MX21ADS
-#include <mach/board-mx21ads.h>
+#ifdef CONFIG_ARCH_MX1
+#include <mach/mx1.h>
+#define UART_PADDR UART1_BASE_ADDR
+#define UART_VADDR IO_ADDRESS(UART1_BASE_ADDR)
#endif
-#ifdef CONFIG_MACH_PCM038
-#include <mach/board-pcm038.h>
+
+#ifdef CONFIG_ARCH_MX25
+#ifdef UART_PADDR
+#error "CONFIG_DEBUG_LL is incompatible with multiple archs"
#endif
-#ifdef CONFIG_MACH_MX31_3DS
-#include <mach/board-mx31pdk.h>
+#include <mach/mx25.h>
+#define UART_PADDR UART1_BASE_ADDR
+#define UART_VADDR MX25_AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
#endif
-#ifdef CONFIG_MACH_QONG
-#include <mach/board-qong.h>
+
+#ifdef CONFIG_ARCH_MX2
+#ifdef UART_PADDR
+#error "CONFIG_DEBUG_LL is incompatible with multiple archs"
#endif
-#ifdef CONFIG_MACH_PCM043
-#include <mach/board-pcm043.h>
+#include <mach/mx2x.h>
+#define UART_PADDR UART1_BASE_ADDR
+#define UART_VADDR AIPI_IO_ADDRESS(UART1_BASE_ADDR)
#endif
-#ifdef CONFIG_MACH_MX27_3DS
-#include <mach/board-mx27pdk.h>
+
+#ifdef CONFIG_ARCH_MX3
+#ifdef UART_PADDR
+#error "CONFIG_DEBUG_LL is incompatible with multiple archs"
#endif
-#ifdef CONFIG_MACH_ARMADILLO5X0
-#include <mach/board-armadillo5x0.h>
+#include <mach/mx3x.h>
+#define UART_PADDR UART1_BASE_ADDR
+#define UART_VADDR AIPS1_IO_ADDRESS(UART1_BASE_ADDR)
#endif
-#ifdef CONFIG_MACH_MX35_3DS
-#include <mach/board-mx35pdk.h>
+
+#ifdef CONFIG_ARCH_MXC91231
+#ifdef UART_PADDR
+#error "CONFIG_DEBUG_LL is incompatible with multiple archs"
#endif
-#ifdef CONFIG_MACH_MX27LITE
-#include <mach/board-mx27lite.h>
+#include <mach/mxc91231.h>
+#define UART_PADDR MXC91231_UART2_BASE_ADDR
+#define UART_VADDR MXC91231_AIPS1_IO_ADDRESS(MXC91231_UART2_BASE_ADDR)
#endif
.macro addruart,rx
mrc p15, 0, \rx, c1, c0
tst \rx, #1 @ MMU enabled?
- ldreq \rx, =MXC_LL_UART_PADDR @ physical
- ldrne \rx, =MXC_LL_UART_VADDR @ virtual
+ ldreq \rx, =UART_PADDR @ physical
+ ldrne \rx, =UART_VADDR @ virtual
.endm
.macro senduart,rd,rx
diff --git a/arch/arm/plat-mxc/include/mach/entry-macro.S b/arch/arm/plat-mxc/include/mach/entry-macro.S
index 5f01d60da84..7cf290efe76 100644
--- a/arch/arm/plat-mxc/include/mach/entry-macro.S
+++ b/arch/arm/plat-mxc/include/mach/entry-macro.S
@@ -18,7 +18,8 @@
.endm
.macro get_irqnr_preamble, base, tmp
- ldr \base, =AVIC_IO_ADDRESS(AVIC_BASE_ADDR)
+ ldr \base, =avic_base
+ ldr \base, [\base]
#ifdef CONFIG_MXC_IRQ_PRIOR
ldr r4, [\base, #AVIC_NIMASK]
#endif
diff --git a/arch/arm/plat-mxc/include/mach/hardware.h b/arch/arm/plat-mxc/include/mach/hardware.h
index 42e4ee37ca1..78db75475f6 100644
--- a/arch/arm/plat-mxc/include/mach/hardware.h
+++ b/arch/arm/plat-mxc/include/mach/hardware.h
@@ -42,6 +42,14 @@
# include <mach/mx1.h>
#endif
+#ifdef CONFIG_ARCH_MX25
+# include <mach/mx25.h>
+#endif
+
+#ifdef CONFIG_ARCH_MXC91231
+# include <mach/mxc91231.h>
+#endif
+
#include <mach/mxc.h>
#endif /* __ASM_ARCH_MXC_HARDWARE_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/imxfb.h b/arch/arm/plat-mxc/include/mach/imxfb.h
index 9f0101157ec..5263506b7dd 100644
--- a/arch/arm/plat-mxc/include/mach/imxfb.h
+++ b/arch/arm/plat-mxc/include/mach/imxfb.h
@@ -2,6 +2,8 @@
* This structure describes the machine which we are running on.
*/
+#include <linux/fb.h>
+
#define PCR_TFT (1 << 31)
#define PCR_COLOR (1 << 30)
#define PCR_PBSIZ_1 (0 << 28)
@@ -13,7 +15,8 @@
#define PCR_BPIX_4 (2 << 25)
#define PCR_BPIX_8 (3 << 25)
#define PCR_BPIX_12 (4 << 25)
-#define PCR_BPIX_16 (4 << 25)
+#define PCR_BPIX_16 (5 << 25)
+#define PCR_BPIX_18 (6 << 25)
#define PCR_PIXPOL (1 << 24)
#define PCR_FLMPOL (1 << 23)
#define PCR_LPPOL (1 << 22)
@@ -46,29 +49,21 @@
#define DMACR_HM(x) (((x) & 0xf) << 16)
#define DMACR_TM(x) ((x) & 0xf)
-struct imx_fb_platform_data {
- u_long pixclock;
-
- u_short xres;
- u_short yres;
-
- u_int nonstd;
- u_char bpp;
- u_char hsync_len;
- u_char left_margin;
- u_char right_margin;
+struct imx_fb_videomode {
+ struct fb_videomode mode;
+ u32 pcr;
+ unsigned char bpp;
+};
- u_char vsync_len;
- u_char upper_margin;
- u_char lower_margin;
- u_char sync;
+struct imx_fb_platform_data {
+ struct imx_fb_videomode *mode;
+ int num_modes;
u_int cmap_greyscale:1,
cmap_inverse:1,
cmap_static:1,
unused:29;
- u_int pcr;
u_int pwmr;
u_int lscr1;
u_int dmacr;
diff --git a/arch/arm/plat-mxc/include/mach/iomux-mx25.h b/arch/arm/plat-mxc/include/mach/iomux-mx25.h
new file mode 100644
index 00000000000..810c47f56e7
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/iomux-mx25.h
@@ -0,0 +1,517 @@
+/*
+ * arch/arm/plat-mxc/include/mach/iomux-mx25.h
+ *
+ * Copyright (C) 2009 by Lothar Wassmann <LW@KARO-electronics.de>
+ *
+ * based on arch/arm/mach-mx25/mx25_pins.h
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ * and
+ * arch/arm/plat-mxc/include/mach/iomux-mx35.h
+ * Copyright (C, NO_PAD_CTRL) 2009 by Jan Weitzel Phytec Messtechnik GmbH <armlinux@phytec.de>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#ifndef __IOMUX_MX25_H__
+#define __IOMUX_MX25_H__
+
+#include <mach/iomux-v3.h>
+
+#ifndef GPIO_PORTA
+#error Please include mach/iomux.h
+#endif
+
+/*
+ *
+ * @brief MX25 I/O Pin List
+ *
+ * @ingroup GPIO_MX25
+ */
+
+#ifndef __ASSEMBLY__
+
+/*
+ * IOMUX/PAD Bit field definitions
+ */
+
+#define MX25_PAD_A10__A10 IOMUX_PAD(0x000, 0x008, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A10__GPIO_4_0 IOMUX_PAD(0x000, 0x008, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_A13__A13 IOMUX_PAD(0x22C, 0x00c, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A13__GPIO_4_1 IOMUX_PAD(0x22C, 0x00c, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_A14__A14 IOMUX_PAD(0x230, 0x010, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A14__GPIO_2_0 IOMUX_PAD(0x230, 0x010, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_A15__A15 IOMUX_PAD(0x234, 0x014, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A15__GPIO_2_1 IOMUX_PAD(0x234, 0x014, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_A16__A16 IOMUX_PAD(0x000, 0x018, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A16__GPIO_2_2 IOMUX_PAD(0x000, 0x018, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_A17__A17 IOMUX_PAD(0x238, 0x01c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A17__GPIO_2_3 IOMUX_PAD(0x238, 0x01c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_A18__A18 IOMUX_PAD(0x23c, 0x020, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A18__GPIO_2_4 IOMUX_PAD(0x23c, 0x020, 0x15, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A18__FEC_COL IOMUX_PAD(0x23c, 0x020, 0x17, 0x504, 0, NO_PAD_CTL)
+
+#define MX25_PAD_A19__A19 IOMUX_PAD(0x240, 0x024, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A19__FEC_RX_ER IOMUX_PAD(0x240, 0x024, 0x17, 0x518, 0, NO_PAD_CTL)
+#define MX25_PAD_A19__GPIO_2_5 IOMUX_PAD(0x240, 0x024, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_A20__A20 IOMUX_PAD(0x244, 0x028, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A20__GPIO_2_6 IOMUX_PAD(0x244, 0x028, 0x15, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A20__FEC_RDATA2 IOMUX_PAD(0x244, 0x028, 0x17, 0x50c, 0, NO_PAD_CTL)
+
+#define MX25_PAD_A21__A21 IOMUX_PAD(0x248, 0x02c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A21__GPIO_2_7 IOMUX_PAD(0x248, 0x02c, 0x15, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A21__FEC_RDATA3 IOMUX_PAD(0x248, 0x02c, 0x17, 0x510, 0, NO_PAD_CTL)
+
+#define MX25_PAD_A22__A22 IOMUX_PAD(0x000, 0x030, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A22__GPIO_2_8 IOMUX_PAD(0x000, 0x030, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_A23__A23 IOMUX_PAD(0x24c, 0x034, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A23__GPIO_2_9 IOMUX_PAD(0x24c, 0x034, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_A24__A24 IOMUX_PAD(0x250, 0x038, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A24__GPIO_2_10 IOMUX_PAD(0x250, 0x038, 0x15, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A24__FEC_RX_CLK IOMUX_PAD(0x250, 0x038, 0x17, 0x514, 0, NO_PAD_CTL)
+
+#define MX25_PAD_A25__A25 IOMUX_PAD(0x254, 0x03c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A25__GPIO_2_11 IOMUX_PAD(0x254, 0x03c, 0x15, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_A25__FEC_CRS IOMUX_PAD(0x254, 0x03c, 0x17, 0x508, 0, NO_PAD_CTL)
+
+#define MX25_PAD_EB0__EB0 IOMUX_PAD(0x258, 0x040, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_EB0__AUD4_TXD IOMUX_PAD(0x258, 0x040, 0x14, 0x464, 0, NO_PAD_CTRL)
+#define MX25_PAD_EB0__GPIO_2_12 IOMUX_PAD(0x258, 0x040, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_EB1__EB1 IOMUX_PAD(0x25c, 0x044, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_EB1__AUD4_RXD IOMUX_PAD(0x25c, 0x044, 0x14, 0x460, 0, NO_PAD_CTRL)
+#define MX25_PAD_EB1__GPIO_2_13 IOMUX_PAD(0x25c, 0x044, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_OE__OE IOMUX_PAD(0x260, 0x048, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_OE__AUD4_TXC IOMUX_PAD(0x260, 0x048, 0x14, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_OE__GPIO_2_14 IOMUX_PAD(0x260, 0x048, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CS0__CS0 IOMUX_PAD(0x000, 0x04c, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CS0__GPIO_4_2 IOMUX_PAD(0x000, 0x04c, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CS1__CS1 IOMUX_PAD(0x000, 0x050, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CS1__GPIO_4_3 IOMUX_PAD(0x000, 0x050, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CS4__CS4 IOMUX_PAD(0x264, 0x054, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CS4__UART5_CTS IOMUX_PAD(0x264, 0x054, 0x13, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CS4__GPIO_3_20 IOMUX_PAD(0x264, 0x054, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CS5__CS5 IOMUX_PAD(0x268, 0x058, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CS5__UART5_RTS IOMUX_PAD(0x268, 0x058, 0x13, 0x574, 0, NO_PAD_CTRL)
+#define MX25_PAD_CS5__GPIO_3_21 IOMUX_PAD(0x268, 0x058, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_NF_CE0__NF_CE0 IOMUX_PAD(0x26c, 0x05c, 0x10, 0, 0, NO_PAD_CTL)
+#define MX25_PAD_NF_CE0__GPIO_3_22 IOMUX_PAD(0x26c, 0x05c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_ECB__ECB IOMUX_PAD(0x270, 0x060, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_ECB__UART5_TXD_MUX IOMUX_PAD(0x270, 0x060, 0x13, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_ECB__GPIO_3_23 IOMUX_PAD(0x270, 0x060, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_LBA__LBA IOMUX_PAD(0x274, 0x064, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LBA__UART5_RXD_MUX IOMUX_PAD(0x274, 0x064, 0x13, 0x578, 0, NO_PAD_CTRL)
+#define MX25_PAD_LBA__GPIO_3_24 IOMUX_PAD(0x274, 0x064, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_BCLK__BCLK IOMUX_PAD(0x000, 0x068, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_BCLK__GPIO_4_4 IOMUX_PAD(0x000, 0x068, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_RW__RW IOMUX_PAD(0x278, 0x06c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_RW__AUD4_TXFS IOMUX_PAD(0x278, 0x06c, 0x14, 0x474, 0, NO_PAD_CTRL)
+#define MX25_PAD_RW__GPIO_3_25 IOMUX_PAD(0x278, 0x06c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_NFWE_B__NFWE_B IOMUX_PAD(0x000, 0x070, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_NFWE_B__GPIO_3_26 IOMUX_PAD(0x000, 0x070, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_NFRE_B__NFRE_B IOMUX_PAD(0x000, 0x074, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_NFRE_B__GPIO_3_27 IOMUX_PAD(0x000, 0x074, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_NFALE__NFALE IOMUX_PAD(0x000, 0x078, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_NFALE__GPIO_3_28 IOMUX_PAD(0x000, 0x078, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_NFCLE__NFCLE IOMUX_PAD(0x000, 0x07c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_NFCLE__GPIO_3_29 IOMUX_PAD(0x000, 0x07c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_NFWP_B__NFWP_B IOMUX_PAD(0x000, 0x080, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_NFWP_B__GPIO_3_30 IOMUX_PAD(0x000, 0x080, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_NFRB__NFRB IOMUX_PAD(0x27c, 0x084, 0x10, 0, 0, PAD_CTL_PKE)
+#define MX25_PAD_NFRB__GPIO_3_31 IOMUX_PAD(0x27c, 0x084, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D15__D15 IOMUX_PAD(0x280, 0x088, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D15__LD16 IOMUX_PAD(0x280, 0x088, 0x01, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D15__GPIO_4_5 IOMUX_PAD(0x280, 0x088, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D14__D14 IOMUX_PAD(0x284, 0x08c, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D14__LD17 IOMUX_PAD(0x284, 0x08c, 0x01, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D14__GPIO_4_6 IOMUX_PAD(0x284, 0x08c, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D13__D13 IOMUX_PAD(0x288, 0x090, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D13__LD18 IOMUX_PAD(0x288, 0x090, 0x01, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D13__GPIO_4_7 IOMUX_PAD(0x288, 0x090, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D12__D12 IOMUX_PAD(0x28c, 0x094, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D12__GPIO_4_8 IOMUX_PAD(0x28c, 0x094, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D11__D11 IOMUX_PAD(0x290, 0x098, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D11__GPIO_4_9 IOMUX_PAD(0x290, 0x098, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D10__D10 IOMUX_PAD(0x294, 0x09c, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D10__GPIO_4_10 IOMUX_PAD(0x294, 0x09c, 0x05, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D10__USBOTG_OC IOMUX_PAD(0x294, 0x09c, 0x06, 0x57c, 0, PAD_CTL_PUS_100K_UP)
+
+#define MX25_PAD_D9__D9 IOMUX_PAD(0x298, 0x0a0, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D9__GPIO_4_11 IOMUX_PAD(0x298, 0x0a0, 0x05, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D9__USBH2_PWR IOMUX_PAD(0x298, 0x0a0, 0x06, 0, 0, PAD_CTL_PKE)
+
+#define MX25_PAD_D8__D8 IOMUX_PAD(0x29c, 0x0a4, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D8__GPIO_4_12 IOMUX_PAD(0x29c, 0x0a4, 0x05, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D8__USBH2_OC IOMUX_PAD(0x29c, 0x0a4, 0x06, 0x580, 0, PAD_CTL_PUS_100K_UP)
+
+#define MX25_PAD_D7__D7 IOMUX_PAD(0x2a0, 0x0a8, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D7__GPIO_4_13 IOMUX_PAD(0x2a0, 0x0a8, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D6__D6 IOMUX_PAD(0x2a4, 0x0ac, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D6__GPIO_4_14 IOMUX_PAD(0x2a4, 0x0ac, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D5__D5 IOMUX_PAD(0x2a8, 0x0b0, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D5__GPIO_4_15 IOMUX_PAD(0x2a8, 0x0b0, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D4__D4 IOMUX_PAD(0x2ac, 0x0b4, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D4__GPIO_4_16 IOMUX_PAD(0x2ac, 0x0b4, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D3__D3 IOMUX_PAD(0x2b0, 0x0b8, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D3__GPIO_4_17 IOMUX_PAD(0x2b0, 0x0b8, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D2__D2 IOMUX_PAD(0x2b4, 0x0bc, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D2__GPIO_4_18 IOMUX_PAD(0x2b4, 0x0bc, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D1__D1 IOMUX_PAD(0x2b8, 0x0c0, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D1__GPIO_4_19 IOMUX_PAD(0x2b8, 0x0c0, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_D0__D0 IOMUX_PAD(0x2bc, 0x0c4, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_D0__GPIO_4_20 IOMUX_PAD(0x2bc, 0x0c4, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_LD0__LD0 IOMUX_PAD(0x2c0, 0x0c8, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD0__CSI_D0 IOMUX_PAD(0x2c0, 0x0c8, 0x12, 0x488, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD0__GPIO_2_15 IOMUX_PAD(0x2c0, 0x0c8, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_LD1__LD1 IOMUX_PAD(0x2c4, 0x0cc, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD1__CSI_D1 IOMUX_PAD(0x2c4, 0x0cc, 0x12, 0x48c, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD1__GPIO_2_16 IOMUX_PAD(0x2c4, 0x0cc, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_LD2__LD2 IOMUX_PAD(0x2c8, 0x0d0, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD2__GPIO_2_17 IOMUX_PAD(0x2c8, 0x0d0, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_LD3__LD3 IOMUX_PAD(0x2cc, 0x0d4, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD3__GPIO_2_18 IOMUX_PAD(0x2cc, 0x0d4, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_LD4__LD4 IOMUX_PAD(0x2d0, 0x0d8, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD4__GPIO_2_19 IOMUX_PAD(0x2d0, 0x0d8, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_LD5__LD5 IOMUX_PAD(0x2d4, 0x0dc, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD5__GPIO_1_19 IOMUX_PAD(0x2d4, 0x0dc, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_LD6__LD6 IOMUX_PAD(0x2d8, 0x0e0, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD6__GPIO_1_20 IOMUX_PAD(0x2d8, 0x0e0, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_LD7__LD7 IOMUX_PAD(0x2dc, 0x0e4, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD7__GPIO_1_21 IOMUX_PAD(0x2dc, 0x0e4, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_LD8__LD8 IOMUX_PAD(0x2e0, 0x0e8, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD8__FEC_TX_ERR IOMUX_PAD(0x2e0, 0x0e8, 0x15, 0, 0, NO_PAD_CTL)
+
+#define MX25_PAD_LD9__LD9 IOMUX_PAD(0x2e4, 0x0ec, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD9__FEC_COL IOMUX_PAD(0x2e4, 0x0ec, 0x15, 0x504, 1, NO_PAD_CTL)
+
+#define MX25_PAD_LD10__LD10 IOMUX_PAD(0x2e8, 0x0f0, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD10__FEC_RX_ER IOMUX_PAD(0x2e8, 0x0f0, 0x15, 0x518, 1, NO_PAD_CTL)
+
+#define MX25_PAD_LD11__LD11 IOMUX_PAD(0x2ec, 0x0f4, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD11__FEC_RDATA2 IOMUX_PAD(0x2ec, 0x0f4, 0x15, 0x50c, 1, NO_PAD_CTL)
+
+#define MX25_PAD_LD12__LD12 IOMUX_PAD(0x2f0, 0x0f8, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD12__FEC_RDATA3 IOMUX_PAD(0x2f0, 0x0f8, 0x15, 0x510, 1, NO_PAD_CTL)
+
+#define MX25_PAD_LD13__LD13 IOMUX_PAD(0x2f4, 0x0fc, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD13__FEC_TDATA2 IOMUX_PAD(0x2f4, 0x0fc, 0x15, 0, 0, NO_PAD_CTL)
+
+#define MX25_PAD_LD14__LD14 IOMUX_PAD(0x2f8, 0x100, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD14__FEC_TDATA3 IOMUX_PAD(0x2f8, 0x100, 0x15, 0, 0, NO_PAD_CTL)
+
+#define MX25_PAD_LD15__LD15 IOMUX_PAD(0x2fc, 0x104, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LD15__FEC_RX_CLK IOMUX_PAD(0x2fc, 0x104, 0x15, 0x514, 1, NO_PAD_CTL)
+
+#define MX25_PAD_HSYNC__HSYNC IOMUX_PAD(0x300, 0x108, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_HSYNC__GPIO_1_22 IOMUX_PAD(0x300, 0x108, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_VSYNC__VSYNC IOMUX_PAD(0x304, 0x10c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_VSYNC__GPIO_1_23 IOMUX_PAD(0x304, 0x10c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_LSCLK__LSCLK IOMUX_PAD(0x308, 0x110, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_LSCLK__GPIO_1_24 IOMUX_PAD(0x308, 0x110, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_OE_ACD__OE_ACD IOMUX_PAD(0x30c, 0x114, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_OE_ACD__GPIO_1_25 IOMUX_PAD(0x30c, 0x114, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CONTRAST__CONTRAST IOMUX_PAD(0x310, 0x118, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CONTRAST__FEC_CRS IOMUX_PAD(0x310, 0x118, 0x15, 0x508, 1, NO_PAD_CTL)
+
+#define MX25_PAD_PWM__PWM IOMUX_PAD(0x314, 0x11c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_PWM__GPIO_1_26 IOMUX_PAD(0x314, 0x11c, 0x15, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_PWM__USBH2_OC IOMUX_PAD(0x314, 0x11c, 0x16, 0x580, 1, PAD_CTL_PUS_100K_UP)
+
+#define MX25_PAD_CSI_D2__CSI_D2 IOMUX_PAD(0x318, 0x120, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSI_D2__UART5_RXD_MUX IOMUX_PAD(0x318, 0x120, 0x11, 0x578, 1, NO_PAD_CTRL)
+#define MX25_PAD_CSI_D2__GPIO_1_27 IOMUX_PAD(0x318, 0x120, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSI_D3__CSI_D3 IOMUX_PAD(0x31c, 0x124, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSI_D3__GPIO_1_28 IOMUX_PAD(0x31c, 0x124, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSI_D4__CSI_D4 IOMUX_PAD(0x320, 0x128, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSI_D4__UART5_RTS IOMUX_PAD(0x320, 0x128, 0x11, 0x574, 1, NO_PAD_CTRL)
+#define MX25_PAD_CSI_D4__GPIO_1_29 IOMUX_PAD(0x320, 0x128, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSI_D5__CSI_D5 IOMUX_PAD(0x324, 0x12c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSI_D5__GPIO_1_30 IOMUX_PAD(0x324, 0x12c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSI_D6__CSI_D6 IOMUX_PAD(0x328, 0x130, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSI_D6__GPIO_1_31 IOMUX_PAD(0x328, 0x130, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSI_D7__CSI_D7 IOMUX_PAD(0x32c, 0x134, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSI_D7__GPIO_1_6 IOMUX_PAD(0x32c, 0x134, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSI_D8__CSI_D8 IOMUX_PAD(0x330, 0x138, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSI_D8__GPIO_1_7 IOMUX_PAD(0x330, 0x138, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSI_D9__CSI_D9 IOMUX_PAD(0x334, 0x13c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSI_D9__GPIO_4_21 IOMUX_PAD(0x334, 0x13c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSI_MCLK__CSI_MCLK IOMUX_PAD(0x338, 0x140, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSI_MCLK__GPIO_1_8 IOMUX_PAD(0x338, 0x140, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSI_VSYNC__CSI_VSYNC IOMUX_PAD(0x33c, 0x144, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSI_VSYNC__GPIO_1_9 IOMUX_PAD(0x33c, 0x144, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSI_HSYNC__CSI_HSYNC IOMUX_PAD(0x340, 0x148, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSI_HSYNC__GPIO_1_10 IOMUX_PAD(0x340, 0x148, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSI_PIXCLK__CSI_PIXCLK IOMUX_PAD(0x344, 0x14c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSI_PIXCLK__GPIO_1_11 IOMUX_PAD(0x344, 0x14c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_I2C1_CLK__I2C1_CLK IOMUX_PAD(0x348, 0x150, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_I2C1_CLK__GPIO_1_12 IOMUX_PAD(0x348, 0x150, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_I2C1_DAT__I2C1_DAT IOMUX_PAD(0x34c, 0x154, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_I2C1_DAT__GPIO_1_13 IOMUX_PAD(0x34c, 0x154, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSPI1_MOSI__CSPI1_MOSI IOMUX_PAD(0x350, 0x158, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSPI1_MOSI__GPIO_1_14 IOMUX_PAD(0x350, 0x158, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSPI1_MISO__CSPI1_MISO IOMUX_PAD(0x354, 0x15c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSPI1_MISO__GPIO_1_15 IOMUX_PAD(0x354, 0x15c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSPI1_SS0__CSPI1_SS0 IOMUX_PAD(0x358, 0x160, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSPI1_SS0__GPIO_1_16 IOMUX_PAD(0x358, 0x160, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSPI1_SS1__CSPI1_SS1 IOMUX_PAD(0x35c, 0x164, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSPI1_SS1__GPIO_1_17 IOMUX_PAD(0x35c, 0x164, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSPI1_SCLK__CSPI1_SCLK IOMUX_PAD(0x360, 0x168, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CSPI1_SCLK__GPIO_1_18 IOMUX_PAD(0x360, 0x168, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CSPI1_RDY__CSPI1_RDY IOMUX_PAD(0x364, 0x16c, 0x10, 0, 0, PAD_CTL_PKE)
+#define MX25_PAD_CSPI1_RDY__GPIO_2_22 IOMUX_PAD(0x364, 0x16c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_UART1_RXD__UART1_RXD IOMUX_PAD(0x368, 0x170, 0x10, 0, 0, PAD_CTL_PUS_100K_DOWN)
+#define MX25_PAD_UART1_RXD__GPIO_4_22 IOMUX_PAD(0x368, 0x170, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_UART1_TXD__UART1_TXD IOMUX_PAD(0x36c, 0x174, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_UART1_TXD__GPIO_4_23 IOMUX_PAD(0x36c, 0x174, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_UART1_RTS__UART1_RTS IOMUX_PAD(0x370, 0x178, 0x10, 0, 0, PAD_CTL_PUS_100K_UP)
+#define MX25_PAD_UART1_RTS__CSI_D0 IOMUX_PAD(0x370, 0x178, 0x11, 0x488, 1, NO_PAD_CTRL)
+#define MX25_PAD_UART1_RTS__GPIO_4_24 IOMUX_PAD(0x370, 0x178, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_UART1_CTS__UART1_CTS IOMUX_PAD(0x374, 0x17c, 0x10, 0, 0, PAD_CTL_PUS_100K_UP)
+#define MX25_PAD_UART1_CTS__CSI_D1 IOMUX_PAD(0x374, 0x17c, 0x11, 0x48c, 1, NO_PAD_CTRL)
+#define MX25_PAD_UART1_CTS__GPIO_4_25 IOMUX_PAD(0x374, 0x17c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_UART2_RXD__UART2_RXD IOMUX_PAD(0x378, 0x180, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_UART2_RXD__GPIO_4_26 IOMUX_PAD(0x378, 0x180, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_UART2_TXD__UART2_TXD IOMUX_PAD(0x37c, 0x184, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_UART2_TXD__GPIO_4_27 IOMUX_PAD(0x37c, 0x184, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_UART2_RTS__UART2_RTS IOMUX_PAD(0x380, 0x188, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_UART2_RTS__FEC_COL IOMUX_PAD(0x380, 0x188, 0x12, 0x504, 2, NO_PAD_CTL)
+#define MX25_PAD_UART2_RTS__GPIO_4_28 IOMUX_PAD(0x380, 0x188, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_UART2_CTS__FEC_RX_ER IOMUX_PAD(0x384, 0x18c, 0x12, 0x518, 2, NO_PAD_CTL)
+#define MX25_PAD_UART2_CTS__UART2_CTS IOMUX_PAD(0x384, 0x18c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_UART2_CTS__GPIO_4_29 IOMUX_PAD(0x384, 0x18c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_SD1_CMD__SD1_CMD IOMUX_PAD(0x388, 0x190, 0x10, 0, 0, PAD_CTL_PUS_47K_UP)
+#define MX25_PAD_SD1_CMD__FEC_RDATA2 IOMUX_PAD(0x388, 0x190, 0x12, 0x50c, 2, NO_PAD_CTL)
+#define MX25_PAD_SD1_CMD__GPIO_2_23 IOMUX_PAD(0x388, 0x190, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_SD1_CLK__SD1_CLK IOMUX_PAD(0x38c, 0x194, 0x10, 0, 0, PAD_CTL_PUS_47K_UP)
+#define MX25_PAD_SD1_CLK__FEC_RDATA3 IOMUX_PAD(0x38c, 0x194, 0x12, 0x510, 2, NO_PAD_CTL)
+#define MX25_PAD_SD1_CLK__GPIO_2_24 IOMUX_PAD(0x38c, 0x194, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_SD1_DATA0__SD1_DATA0 IOMUX_PAD(0x390, 0x198, 0x10, 0, 0, PAD_CTL_PUS_47K_UP)
+#define MX25_PAD_SD1_DATA0__GPIO_2_25 IOMUX_PAD(0x390, 0x198, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_SD1_DATA1__SD1_DATA1 IOMUX_PAD(0x394, 0x19c, 0x10, 0, 0, PAD_CTL_PUS_47K_UP)
+#define MX25_PAD_SD1_DATA1__AUD7_RXD IOMUX_PAD(0x394, 0x19c, 0x13, 0x478, 0, NO_PAD_CTRL)
+#define MX25_PAD_SD1_DATA1__GPIO_2_26 IOMUX_PAD(0x394, 0x19c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_SD1_DATA2__SD1_DATA2 IOMUX_PAD(0x398, 0x1a0, 0x10, 0, 0, PAD_CTL_PUS_47K_UP)
+#define MX25_PAD_SD1_DATA2__FEC_RX_CLK IOMUX_PAD(0x398, 0x1a0, 0x15, 0x514, 2, NO_PAD_CTL)
+#define MX25_PAD_SD1_DATA2__GPIO_2_27 IOMUX_PAD(0x398, 0x1a0, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_SD1_DATA3__SD1_DATA3 IOMUX_PAD(0x39c, 0x1a4, 0x10, 0, 0, PAD_CTL_PUS_47K_UP)
+#define MX25_PAD_SD1_DATA3__FEC_CRS IOMUX_PAD(0x39c, 0x1a4, 0x10, 0x508, 2, NO_PAD_CTL)
+#define MX25_PAD_SD1_DATA3__GPIO_2_28 IOMUX_PAD(0x39c, 0x1a4, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_KPP_ROW0__KPP_ROW0 IOMUX_PAD(0x3a0, 0x1a8, 0x10, 0, 0, PAD_CTL_PKE)
+#define MX25_PAD_KPP_ROW0__GPIO_2_29 IOMUX_PAD(0x3a0, 0x1a8, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_KPP_ROW1__KPP_ROW1 IOMUX_PAD(0x3a4, 0x1ac, 0x10, 0, 0, PAD_CTL_PKE)
+#define MX25_PAD_KPP_ROW1__GPIO_2_30 IOMUX_PAD(0x3a4, 0x1ac, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_KPP_ROW2__KPP_ROW2 IOMUX_PAD(0x3a8, 0x1b0, 0x10, 0, 0, PAD_CTL_PKE)
+#define MX25_PAD_KPP_ROW2__CSI_D0 IOMUX_PAD(0x3a8, 0x1b0, 0x13, 0x488, 2, NO_PAD_CTRL)
+#define MX25_PAD_KPP_ROW2__GPIO_2_31 IOMUX_PAD(0x3a8, 0x1b0, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_KPP_ROW3__KPP_ROW3 IOMUX_PAD(0x3ac, 0x1b4, 0x10, 0, 0, PAD_CTL_PKE)
+#define MX25_PAD_KPP_ROW3__CSI_LD1 IOMUX_PAD(0x3ac, 0x1b4, 0x13, 0x48c, 2, NO_PAD_CTRL)
+#define MX25_PAD_KPP_ROW3__GPIO_3_0 IOMUX_PAD(0x3ac, 0x1b4, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_KPP_COL0__KPP_COL0 IOMUX_PAD(0x3b0, 0x1b8, 0x10, 0, 0, PAD_CTL_PKE | PAD_CTL_ODE)
+#define MX25_PAD_KPP_COL0__GPIO_3_1 IOMUX_PAD(0x3b0, 0x1b8, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_KPP_COL1__KPP_COL1 IOMUX_PAD(0x3b4, 0x1bc, 0x10, 0, 0, PAD_CTL_PKE | PAD_CTL_ODE)
+#define MX25_PAD_KPP_COL1__GPIO_3_2 IOMUX_PAD(0x3b4, 0x1bc, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_KPP_COL2__KPP_COL2 IOMUX_PAD(0x3b8, 0x1c0, 0x10, 0, 0, PAD_CTL_PKE | PAD_CTL_ODE)
+#define MX25_PAD_KPP_COL2__GPIO_3_3 IOMUX_PAD(0x3b8, 0x1c0, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_KPP_COL3__KPP_COL3 IOMUX_PAD(0x3bc, 0x1c4, 0x10, 0, 0, PAD_CTL_PKE | PAD_CTL_ODE)
+#define MX25_PAD_KPP_COL3__GPIO_3_4 IOMUX_PAD(0x3bc, 0x1c4, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_FEC_MDC__FEC_MDC IOMUX_PAD(0x3c0, 0x1c8, 0x10, 0, 0, NO_PAD_CTL)
+#define MX25_PAD_FEC_MDC__AUD4_TXD IOMUX_PAD(0x3c0, 0x1c8, 0x12, 0x464, 1, NO_PAD_CTRL)
+#define MX25_PAD_FEC_MDC__GPIO_3_5 IOMUX_PAD(0x3c0, 0x1c8, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_FEC_MDIO__FEC_MDIO IOMUX_PAD(0x3c4, 0x1cc, 0x10, 0, 0, PAD_CTL_HYS | PAD_CTL_PUS_22K_UP)
+#define MX25_PAD_FEC_MDIO__AUD4_RXD IOMUX_PAD(0x3c4, 0x1cc, 0x12, 0x460, 1, NO_PAD_CTRL)
+#define MX25_PAD_FEC_MDIO__GPIO_3_6 IOMUX_PAD(0x3c4, 0x1cc, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_FEC_TDATA0__FEC_TDATA0 IOMUX_PAD(0x3c8, 0x1d0, 0x10, 0, 0, NO_PAD_CTL)
+#define MX25_PAD_FEC_TDATA0__GPIO_3_7 IOMUX_PAD(0x3c8, 0x1d0, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_FEC_TDATA1__FEC_TDATA1 IOMUX_PAD(0x3cc, 0x1d4, 0x10, 0, 0, NO_PAD_CTL)
+#define MX25_PAD_FEC_TDATA1__AUD4_TXFS IOMUX_PAD(0x3cc, 0x1d4, 0x12, 0x474, 1, NO_PAD_CTRL)
+#define MX25_PAD_FEC_TDATA1__GPIO_3_8 IOMUX_PAD(0x3cc, 0x1d4, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_FEC_TX_EN__FEC_TX_EN IOMUX_PAD(0x3d0, 0x1d8, 0x10, 0, 0, NO_PAD_CTL)
+#define MX25_PAD_FEC_TX_EN__GPIO_3_9 IOMUX_PAD(0x3d0, 0x1d8, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_FEC_RDATA0__FEC_RDATA0 IOMUX_PAD(0x3d4, 0x1dc, 0x10, 0, 0, PAD_CTL_PUS_100K_DOWN | NO_PAD_CTL)
+#define MX25_PAD_FEC_RDATA0__GPIO_3_10 IOMUX_PAD(0x3d4, 0x1dc, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_FEC_RDATA1__FEC_RDATA1 IOMUX_PAD(0x3d8, 0x1e0, 0x10, 0, 0, PAD_CTL_PUS_100K_DOWN | NO_PAD_CTL)
+#define MX25_PAD_FEC_RDATA1__GPIO_3_11 IOMUX_PAD(0x3d8, 0x1e0, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_FEC_RX_DV__FEC_RX_DV IOMUX_PAD(0x3dc, 0x1e4, 0x10, 0, 0, PAD_CTL_PUS_100K_DOWN | NO_PAD_CTL)
+#define MX25_PAD_FEC_RX_DV__CAN2_RX IOMUX_PAD(0x3dc, 0x1e4, 0x14, 0x484, 0, PAD_CTL_PUS_22K_UP)
+#define MX25_PAD_FEC_RX_DV__GPIO_3_12 IOMUX_PAD(0x3dc, 0x1e4, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_FEC_TX_CLK__FEC_TX_CLK IOMUX_PAD(0x3e0, 0x1e8, 0x10, 0, 0, PAD_CTL_HYS | PAD_CTL_PUS_100K_DOWN)
+#define MX25_PAD_FEC_TX_CLK__GPIO_3_13 IOMUX_PAD(0x3e0, 0x1e8, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_RTCK__RTCK IOMUX_PAD(0x3e4, 0x1ec, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_RTCK__OWIRE IOMUX_PAD(0x3e4, 0x1ec, 0x11, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_RTCK__GPIO_3_14 IOMUX_PAD(0x3e4, 0x1ec, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_DE_B__DE_B IOMUX_PAD(0x3ec, 0x1f0, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_DE_B__GPIO_2_20 IOMUX_PAD(0x3ec, 0x1f0, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_TDO__TDO IOMUX_PAD(0x3e8, 0x000, 0x00, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_GPIO_A__GPIO_A IOMUX_PAD(0x3f0, 0x1f4, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_GPIO_A__CAN1_TX IOMUX_PAD(0x3f0, 0x1f4, 0x16, 0, 0, PAD_CTL_PUS_22K_UP)
+#define MX25_PAD_GPIO_A__USBOTG_PWR IOMUX_PAD(0x3f0, 0x1f4, 0x12, 0, 0, PAD_CTL_PKE)
+
+#define MX25_PAD_GPIO_B__GPIO_B IOMUX_PAD(0x3f4, 0x1f8, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_GPIO_B__CAN1_RX IOMUX_PAD(0x3f4, 0x1f8, 0x16, 0x480, 1, PAD_CTL_PUS_22K)
+#define MX25_PAD_GPIO_B__USBOTG_OC IOMUX_PAD(0x3f4, 0x1f8, 0x12, 0x57c, 1, PAD_CTL_PUS_100K_UP)
+
+#define MX25_PAD_GPIO_C__GPIO_C IOMUX_PAD(0x3f8, 0x1fc, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_GPIO_C__CAN2_TX IOMUX_PAD(0x3f8, 0x1fc, 0x16, 0, 0, PAD_CTL_PUS_22K_UP)
+
+#define MX25_PAD_GPIO_D__GPIO_D IOMUX_PAD(0x3fc, 0x200, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_GPIO_D__CAN2_RX IOMUX_PAD(0x3fc, 0x200, 0x16, 0x484, 1, PAD_CTL_PUS_22K_UP)
+
+#define MX25_PAD_GPIO_E__GPIO_E IOMUX_PAD(0x400, 0x204, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_GPIO_E__AUD7_TXD IOMUX_PAD(0x400, 0x204, 0x14, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_GPIO_F__GPIO_F IOMUX_PAD(0x404, 0x208, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_GPIO_F__AUD7_TXC IOMUX_PAD(0x404, 0x208, 0x14, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_EXT_ARMCLK__EXT_ARMCLK IOMUX_PAD(0x000, 0x20c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_EXT_ARMCLK__GPIO_3_15 IOMUX_PAD(0x000, 0x20c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_UPLL_BYPCLK__UPLL_BYPCLK IOMUX_PAD(0x000, 0x210, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_UPLL_BYPCLK__GPIO_3_16 IOMUX_PAD(0x000, 0x210, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_VSTBY_REQ__VSTBY_REQ IOMUX_PAD(0x408, 0x214, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_VSTBY_REQ__AUD7_TXFS IOMUX_PAD(0x408, 0x214, 0x14, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_VSTBY_REQ__GPIO_3_17 IOMUX_PAD(0x408, 0x214, 0x15, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_VSTBY_ACK__VSTBY_ACK IOMUX_PAD(0x40c, 0x218, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_VSTBY_ACK__GPIO_3_18 IOMUX_PAD(0x40c, 0x218, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_POWER_FAIL__POWER_FAIL IOMUX_PAD(0x410, 0x21c, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_POWER_FAIL__AUD7_RXD IOMUX_PAD(0x410, 0x21c, 0x14, 0x478, 1, NO_PAD_CTRL)
+#define MX25_PAD_POWER_FAIL__GPIO_3_19 IOMUX_PAD(0x410, 0x21c, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CLKO__CLKO IOMUX_PAD(0x414, 0x220, 0x10, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CLKO__GPIO_2_21 IOMUX_PAD(0x414, 0x220, 0x15, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_BOOT_MODE0__BOOT_MODE0 IOMUX_PAD(0x000, 0x224, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_BOOT_MODE0__GPIO_4_30 IOMUX_PAD(0x000, 0x224, 0x05, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_BOOT_MODE1__BOOT_MODE1 IOMUX_PAD(0x000, 0x228, 0x00, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_BOOT_MODE1__GPIO_4_31 IOMUX_PAD(0x000, 0x228, 0x05, 0, 0, NO_PAD_CTRL)
+
+#define MX25_PAD_CTL_GRP_DVS_MISC IOMUX_PAD(0x418, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DSE_FEC IOMUX_PAD(0x41c, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DVS_JTAG IOMUX_PAD(0x420, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DSE_NFC IOMUX_PAD(0x424, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DSE_CSI IOMUX_PAD(0x428, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DSE_WEIM IOMUX_PAD(0x42c, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DSE_DDR IOMUX_PAD(0x430, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DVS_CRM IOMUX_PAD(0x434, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DSE_KPP IOMUX_PAD(0x438, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DSE_SDHC1 IOMUX_PAD(0x43c, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DSE_LCD IOMUX_PAD(0x440, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DSE_UART IOMUX_PAD(0x444, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DVS_NFC IOMUX_PAD(0x448, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DVS_CSI IOMUX_PAD(0x44c, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DSE_CSPI1 IOMUX_PAD(0x450, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DDRTYPE IOMUX_PAD(0x454, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DVS_SDHC1 IOMUX_PAD(0x458, 0x000, 0, 0, 0, NO_PAD_CTRL)
+#define MX25_PAD_CTL_GRP_DVS_LCD IOMUX_PAD(0x45c, 0x000, 0, 0, 0, NO_PAD_CTRL)
+
+#endif // __ASSEMBLY__
+#endif // __IOMUX_MX25_H__
diff --git a/arch/arm/plat-mxc/include/mach/iomux-mx3.h b/arch/arm/plat-mxc/include/mach/iomux-mx3.h
index 2eb182f7387..446f8676381 100644
--- a/arch/arm/plat-mxc/include/mach/iomux-mx3.h
+++ b/arch/arm/plat-mxc/include/mach/iomux-mx3.h
@@ -635,6 +635,19 @@ enum iomux_pins {
#define MX31_PIN_USBOTG_DIR__USBOTG_DIR IOMUX_MODE(MX31_PIN_USBOTG_DIR, IOMUX_CONFIG_FUNC)
#define MX31_PIN_USBOTG_NXT__USBOTG_NXT IOMUX_MODE(MX31_PIN_USBOTG_NXT, IOMUX_CONFIG_FUNC)
#define MX31_PIN_USBOTG_STP__USBOTG_STP IOMUX_MODE(MX31_PIN_USBOTG_STP, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_CSPI1_MOSI__USBH1_RXDM IOMUX_MODE(MX31_PIN_CSPI1_MOSI, IOMUX_CONFIG_ALT1)
+#define MX31_PIN_CSPI1_MISO__USBH1_RXDP IOMUX_MODE(MX31_PIN_CSPI1_MISO, IOMUX_CONFIG_ALT1)
+#define MX31_PIN_CSPI1_SS0__USBH1_TXDM IOMUX_MODE(MX31_PIN_CSPI1_SS0, IOMUX_CONFIG_ALT1)
+#define MX31_PIN_CSPI1_SS1__USBH1_TXDP IOMUX_MODE(MX31_PIN_CSPI1_SS1, IOMUX_CONFIG_ALT1)
+#define MX31_PIN_CSPI1_SS2__USBH1_RCV IOMUX_MODE(MX31_PIN_CSPI1_SS2, IOMUX_CONFIG_ALT1)
+#define MX31_PIN_CSPI1_SCLK__USBH1_OEB IOMUX_MODE(MX31_PIN_CSPI1_SCLK, IOMUX_CONFIG_ALT1)
+#define MX31_PIN_CSPI1_SPI_RDY__USBH1_FS IOMUX_MODE(MX31_PIN_CSPI1_SPI_RDY, IOMUX_CONFIG_ALT1)
+#define MX31_PIN_USBH2_DATA0__USBH2_DATA0 IOMUX_MODE(MX31_PIN_USBH2_DATA0, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_USBH2_DATA1__USBH2_DATA1 IOMUX_MODE(MX31_PIN_USBH2_DATA1, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_USBH2_CLK__USBH2_CLK IOMUX_MODE(MX31_PIN_USBH2_CLK, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_USBH2_DIR__USBH2_DIR IOMUX_MODE(MX31_PIN_USBH2_DIR, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_USBH2_NXT__USBH2_NXT IOMUX_MODE(MX31_PIN_USBH2_NXT, IOMUX_CONFIG_FUNC)
+#define MX31_PIN_USBH2_STP__USBH2_STP IOMUX_MODE(MX31_PIN_USBH2_STP, IOMUX_CONFIG_FUNC)
#define MX31_PIN_USB_OC__GPIO1_30 IOMUX_MODE(MX31_PIN_USB_OC, IOMUX_CONFIG_GPIO)
#define MX31_PIN_I2C_DAT__I2C1_SDA IOMUX_MODE(MX31_PIN_I2C_DAT, IOMUX_CONFIG_FUNC)
#define MX31_PIN_I2C_CLK__I2C1_SCL IOMUX_MODE(MX31_PIN_I2C_CLK, IOMUX_CONFIG_FUNC)
@@ -669,6 +682,18 @@ enum iomux_pins {
#define MX31_PIN_GPIO3_0__GPIO3_0 IOMUX_MODE(MX31_PIN_GPIO3_0, IOMUX_CONFIG_GPIO)
#define MX31_PIN_GPIO3_1__GPIO3_1 IOMUX_MODE(MX31_PIN_GPIO3_1, IOMUX_CONFIG_GPIO)
#define MX31_PIN_TXD2__GPIO1_28 IOMUX_MODE(MX31_PIN_TXD2, IOMUX_CONFIG_GPIO)
+#define MX31_PIN_GPIO1_0__GPIO1_0 IOMUX_MODE(MX31_PIN_GPIO1_0, IOMUX_CONFIG_GPIO)
+#define MX31_PIN_SVEN0__GPIO2_0 IOMUX_MODE(MX31_PIN_SVEN0, IOMUX_CONFIG_GPIO)
+#define MX31_PIN_STX0__GPIO2_1 IOMUX_MODE(MX31_PIN_STX0, IOMUX_CONFIG_GPIO)
+#define MX31_PIN_SRX0__GPIO2_2 IOMUX_MODE(MX31_PIN_SRX0, IOMUX_CONFIG_GPIO)
+#define MX31_PIN_SIMPD0__GPIO2_3 IOMUX_MODE(MX31_PIN_SIMPD0, IOMUX_CONFIG_GPIO)
+#define MX31_PIN_DTR_DCE1__GPIO2_8 IOMUX_MODE(MX31_PIN_DTR_DCE1, IOMUX_CONFIG_GPIO)
+#define MX31_PIN_DSR_DCE1__GPIO2_9 IOMUX_MODE(MX31_PIN_DSR_DCE1, IOMUX_CONFIG_GPIO)
+#define MX31_PIN_RI_DCE1__GPIO2_10 IOMUX_MODE(MX31_PIN_RI_DCE1, IOMUX_CONFIG_GPIO)
+#define MX31_PIN_DCD_DCE1__GPIO2_11 IOMUX_MODE(MX31_PIN_DCD_DCE1, IOMUX_CONFIG_GPIO)
+#define MX31_PIN_STXD5__GPIO1_21 IOMUX_MODE(MX31_PIN_STXD5, IOMUX_CONFIG_GPIO)
+#define MX31_PIN_SRXD5__GPIO1_22 IOMUX_MODE(MX31_PIN_SRXD5, IOMUX_CONFIG_GPIO)
+
/*XXX: The SS0, SS1, SS2, SS3 lines of spi3 are multiplexed by cspi2_ss0, cspi2_ss1, cspi1_ss0
* cspi1_ss1*/
diff --git a/arch/arm/plat-mxc/include/mach/iomux-mxc91231.h b/arch/arm/plat-mxc/include/mach/iomux-mxc91231.h
new file mode 100644
index 00000000000..9f13061192c
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/iomux-mxc91231.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2008 by Sascha Hauer <kernel@pengutronix.de>
+ * Copyright (C) 2009 by Dmitriy Taychenachev <dimichxp@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __MACH_IOMUX_MXC91231_H__
+#define __MACH_IOMUX_MXC91231_H__
+
+/*
+ * various IOMUX output functions
+ */
+
+#define IOMUX_OCONFIG_GPIO (0 << 4) /* used as GPIO */
+#define IOMUX_OCONFIG_FUNC (1 << 4) /* used as function */
+#define IOMUX_OCONFIG_ALT1 (2 << 4) /* used as alternate function 1 */
+#define IOMUX_OCONFIG_ALT2 (3 << 4) /* used as alternate function 2 */
+#define IOMUX_OCONFIG_ALT3 (4 << 4) /* used as alternate function 3 */
+#define IOMUX_OCONFIG_ALT4 (5 << 4) /* used as alternate function 4 */
+#define IOMUX_OCONFIG_ALT5 (6 << 4) /* used as alternate function 5 */
+#define IOMUX_OCONFIG_ALT6 (7 << 4) /* used as alternate function 6 */
+#define IOMUX_ICONFIG_NONE 0 /* not configured for input */
+#define IOMUX_ICONFIG_GPIO 1 /* used as GPIO */
+#define IOMUX_ICONFIG_FUNC 2 /* used as function */
+#define IOMUX_ICONFIG_ALT1 4 /* used as alternate function 1 */
+#define IOMUX_ICONFIG_ALT2 8 /* used as alternate function 2 */
+
+#define IOMUX_CONFIG_GPIO (IOMUX_OCONFIG_GPIO | IOMUX_ICONFIG_GPIO)
+#define IOMUX_CONFIG_FUNC (IOMUX_OCONFIG_FUNC | IOMUX_ICONFIG_FUNC)
+#define IOMUX_CONFIG_ALT1 (IOMUX_OCONFIG_ALT1 | IOMUX_ICONFIG_ALT1)
+#define IOMUX_CONFIG_ALT2 (IOMUX_OCONFIG_ALT2 | IOMUX_ICONFIG_ALT2)
+
+/*
+ * setups a single pin:
+ * - reserves the pin so that it is not claimed by another driver
+ * - setups the iomux according to the configuration
+ * - if the pin is configured as a GPIO, we claim it throug kernel gpiolib
+ */
+int mxc_iomux_alloc_pin(const unsigned int pin_mode, const char *label);
+/*
+ * setups mutliple pins
+ * convenient way to call the above function with tables
+ */
+int mxc_iomux_setup_multiple_pins(unsigned int *pin_list, unsigned count,
+ const char *label);
+
+/*
+ * releases a single pin:
+ * - make it available for a future use by another driver
+ * - frees the GPIO if the pin was configured as GPIO
+ * - DOES NOT reconfigure the IOMUX in its reset state
+ */
+void mxc_iomux_release_pin(const unsigned int pin_mode);
+/*
+ * releases multiple pins
+ * convenvient way to call the above function with tables
+ */
+void mxc_iomux_release_multiple_pins(unsigned int *pin_list, int count);
+
+#define MUX_SIDE_AP (0)
+#define MUX_SIDE_SP (1)
+
+#define MUX_SIDE_SHIFT (26)
+#define MUX_SIDE_MASK (0x1 << MUX_SIDE_SHIFT)
+
+#define MUX_GPIO_PORT_SHIFT (23)
+#define MUX_GPIO_PORT_MASK (0x7 << MUX_GPIO_PORT_SHIFT)
+
+#define MUX_GPIO_PIN_SHIFT (20)
+#define MUX_GPIO_PIN_MASK (0x1f << MUX_GPIO_PIN_SHIFT)
+
+#define MUX_REG_SHIFT (15)
+#define MUX_REG_MASK (0x1f << MUX_REG_SHIFT)
+
+#define MUX_FIELD_SHIFT (13)
+#define MUX_FIELD_MASK (0x3 << MUX_FIELD_SHIFT)
+
+#define MUX_PADGRP_SHIFT (8)
+#define MUX_PADGRP_MASK (0x1f << MUX_PADGRP_SHIFT)
+
+#define MUX_PIN_MASK (0xffffff << 8)
+
+#define GPIO_PORT_MAX (3)
+
+#define IOMUX_PIN(side, gport, gpin, ctlreg, ctlfield, padgrp) \
+ (((side) << MUX_SIDE_SHIFT) | \
+ (gport << MUX_GPIO_PORT_SHIFT) | \
+ ((gpin) << MUX_GPIO_PIN_SHIFT) | \
+ ((ctlreg) << MUX_REG_SHIFT) | \
+ ((ctlfield) << MUX_FIELD_SHIFT) | \
+ ((padgrp) << MUX_PADGRP_SHIFT))
+
+#define MUX_MODE_OUT_SHIFT (4)
+#define MUX_MODE_IN_SHIFT (0)
+#define MUX_MODE_SHIFT (0)
+#define MUX_MODE_MASK (0xff << MUX_MODE_SHIFT)
+
+#define IOMUX_MODE(pin, mode) \
+ (pin | (mode << MUX_MODE_SHIFT))
+
+enum iomux_pins {
+ /* AP Side pins */
+ MXC91231_PIN_AP_CLE = IOMUX_PIN(0, 0, 0, 0, 0, 24),
+ MXC91231_PIN_AP_ALE = IOMUX_PIN(0, 0, 1, 0, 1, 24),
+ MXC91231_PIN_AP_CE_B = IOMUX_PIN(0, 0, 2, 0, 2, 24),
+ MXC91231_PIN_AP_RE_B = IOMUX_PIN(0, 0, 3, 0, 3, 24),
+ MXC91231_PIN_AP_WE_B = IOMUX_PIN(0, 0, 4, 1, 0, 24),
+ MXC91231_PIN_AP_WP_B = IOMUX_PIN(0, 0, 5, 1, 1, 24),
+ MXC91231_PIN_AP_BSY_B = IOMUX_PIN(0, 0, 6, 1, 2, 24),
+ MXC91231_PIN_AP_U1_TXD = IOMUX_PIN(0, 0, 7, 1, 3, 28),
+ MXC91231_PIN_AP_U1_RXD = IOMUX_PIN(0, 0, 8, 2, 0, 28),
+ MXC91231_PIN_AP_U1_RTS_B = IOMUX_PIN(0, 0, 9, 2, 1, 28),
+ MXC91231_PIN_AP_U1_CTS_B = IOMUX_PIN(0, 0, 10, 2, 2, 28),
+ MXC91231_PIN_AP_AD1_TXD = IOMUX_PIN(0, 0, 11, 2, 3, 9),
+ MXC91231_PIN_AP_AD1_RXD = IOMUX_PIN(0, 0, 12, 3, 0, 9),
+ MXC91231_PIN_AP_AD1_TXC = IOMUX_PIN(0, 0, 13, 3, 1, 9),
+ MXC91231_PIN_AP_AD1_TXFS = IOMUX_PIN(0, 0, 14, 3, 2, 9),
+ MXC91231_PIN_AP_AD2_TXD = IOMUX_PIN(0, 0, 15, 3, 3, 9),
+ MXC91231_PIN_AP_AD2_RXD = IOMUX_PIN(0, 0, 16, 4, 0, 9),
+ MXC91231_PIN_AP_AD2_TXC = IOMUX_PIN(0, 0, 17, 4, 1, 9),
+ MXC91231_PIN_AP_AD2_TXFS = IOMUX_PIN(0, 0, 18, 4, 2, 9),
+ MXC91231_PIN_AP_OWDAT = IOMUX_PIN(0, 0, 19, 4, 3, 28),
+ MXC91231_PIN_AP_IPU_LD17 = IOMUX_PIN(0, 0, 20, 5, 0, 28),
+ MXC91231_PIN_AP_IPU_D3_VSYNC = IOMUX_PIN(0, 0, 21, 5, 1, 28),
+ MXC91231_PIN_AP_IPU_D3_HSYNC = IOMUX_PIN(0, 0, 22, 5, 2, 28),
+ MXC91231_PIN_AP_IPU_D3_CLK = IOMUX_PIN(0, 0, 23, 5, 3, 28),
+ MXC91231_PIN_AP_IPU_D3_DRDY = IOMUX_PIN(0, 0, 24, 6, 0, 28),
+ MXC91231_PIN_AP_IPU_D3_CONTR = IOMUX_PIN(0, 0, 25, 6, 1, 28),
+ MXC91231_PIN_AP_IPU_D0_CS = IOMUX_PIN(0, 0, 26, 6, 2, 28),
+ MXC91231_PIN_AP_IPU_LD16 = IOMUX_PIN(0, 0, 27, 6, 3, 28),
+ MXC91231_PIN_AP_IPU_D2_CS = IOMUX_PIN(0, 0, 28, 7, 0, 28),
+ MXC91231_PIN_AP_IPU_PAR_RS = IOMUX_PIN(0, 0, 29, 7, 1, 28),
+ MXC91231_PIN_AP_IPU_D3_PS = IOMUX_PIN(0, 0, 30, 7, 2, 28),
+ MXC91231_PIN_AP_IPU_D3_CLS = IOMUX_PIN(0, 0, 31, 7, 3, 28),
+ MXC91231_PIN_AP_IPU_RD = IOMUX_PIN(0, 1, 0, 8, 0, 28),
+ MXC91231_PIN_AP_IPU_WR = IOMUX_PIN(0, 1, 1, 8, 1, 28),
+ MXC91231_PIN_AP_IPU_LD0 = IOMUX_PIN(0, 7, 0, 8, 2, 28),
+ MXC91231_PIN_AP_IPU_LD1 = IOMUX_PIN(0, 7, 0, 8, 3, 28),
+ MXC91231_PIN_AP_IPU_LD2 = IOMUX_PIN(0, 7, 0, 9, 0, 28),
+ MXC91231_PIN_AP_IPU_LD3 = IOMUX_PIN(0, 1, 2, 9, 1, 28),
+ MXC91231_PIN_AP_IPU_LD4 = IOMUX_PIN(0, 1, 3, 9, 2, 28),
+ MXC91231_PIN_AP_IPU_LD5 = IOMUX_PIN(0, 1, 4, 9, 3, 28),
+ MXC91231_PIN_AP_IPU_LD6 = IOMUX_PIN(0, 1, 5, 10, 0, 28),
+ MXC91231_PIN_AP_IPU_LD7 = IOMUX_PIN(0, 1, 6, 10, 1, 28),
+ MXC91231_PIN_AP_IPU_LD8 = IOMUX_PIN(0, 1, 7, 10, 2, 28),
+ MXC91231_PIN_AP_IPU_LD9 = IOMUX_PIN(0, 1, 8, 10, 3, 28),
+ MXC91231_PIN_AP_IPU_LD10 = IOMUX_PIN(0, 1, 9, 11, 0, 28),
+ MXC91231_PIN_AP_IPU_LD11 = IOMUX_PIN(0, 1, 10, 11, 1, 28),
+ MXC91231_PIN_AP_IPU_LD12 = IOMUX_PIN(0, 1, 11, 11, 2, 28),
+ MXC91231_PIN_AP_IPU_LD13 = IOMUX_PIN(0, 1, 12, 11, 3, 28),
+ MXC91231_PIN_AP_IPU_LD14 = IOMUX_PIN(0, 1, 13, 12, 0, 28),
+ MXC91231_PIN_AP_IPU_LD15 = IOMUX_PIN(0, 1, 14, 12, 1, 28),
+ MXC91231_PIN_AP_KPROW4 = IOMUX_PIN(0, 7, 0, 12, 2, 10),
+ MXC91231_PIN_AP_KPROW5 = IOMUX_PIN(0, 1, 16, 12, 3, 10),
+ MXC91231_PIN_AP_GPIO_AP_B17 = IOMUX_PIN(0, 1, 17, 13, 0, 10),
+ MXC91231_PIN_AP_GPIO_AP_B18 = IOMUX_PIN(0, 1, 18, 13, 1, 10),
+ MXC91231_PIN_AP_KPCOL3 = IOMUX_PIN(0, 1, 19, 13, 2, 11),
+ MXC91231_PIN_AP_KPCOL4 = IOMUX_PIN(0, 1, 20, 13, 3, 11),
+ MXC91231_PIN_AP_KPCOL5 = IOMUX_PIN(0, 1, 21, 14, 0, 11),
+ MXC91231_PIN_AP_GPIO_AP_B22 = IOMUX_PIN(0, 1, 22, 14, 1, 11),
+ MXC91231_PIN_AP_GPIO_AP_B23 = IOMUX_PIN(0, 1, 23, 14, 2, 11),
+ MXC91231_PIN_AP_CSI_D0 = IOMUX_PIN(0, 1, 24, 14, 3, 21),
+ MXC91231_PIN_AP_CSI_D1 = IOMUX_PIN(0, 1, 25, 15, 0, 21),
+ MXC91231_PIN_AP_CSI_D2 = IOMUX_PIN(0, 1, 26, 15, 1, 21),
+ MXC91231_PIN_AP_CSI_D3 = IOMUX_PIN(0, 1, 27, 15, 2, 21),
+ MXC91231_PIN_AP_CSI_D4 = IOMUX_PIN(0, 1, 28, 15, 3, 21),
+ MXC91231_PIN_AP_CSI_D5 = IOMUX_PIN(0, 1, 29, 16, 0, 21),
+ MXC91231_PIN_AP_CSI_D6 = IOMUX_PIN(0, 1, 30, 16, 1, 21),
+ MXC91231_PIN_AP_CSI_D7 = IOMUX_PIN(0, 1, 31, 16, 2, 21),
+ MXC91231_PIN_AP_CSI_D8 = IOMUX_PIN(0, 2, 0, 16, 3, 21),
+ MXC91231_PIN_AP_CSI_D9 = IOMUX_PIN(0, 2, 1, 17, 0, 21),
+ MXC91231_PIN_AP_CSI_MCLK = IOMUX_PIN(0, 2, 2, 17, 1, 21),
+ MXC91231_PIN_AP_CSI_VSYNC = IOMUX_PIN(0, 2, 3, 17, 2, 21),
+ MXC91231_PIN_AP_CSI_HSYNC = IOMUX_PIN(0, 2, 4, 17, 3, 21),
+ MXC91231_PIN_AP_CSI_PIXCLK = IOMUX_PIN(0, 2, 5, 18, 0, 21),
+ MXC91231_PIN_AP_I2CLK = IOMUX_PIN(0, 2, 6, 18, 1, 12),
+ MXC91231_PIN_AP_I2DAT = IOMUX_PIN(0, 2, 7, 18, 2, 12),
+ MXC91231_PIN_AP_GPIO_AP_C8 = IOMUX_PIN(0, 2, 8, 18, 3, 9),
+ MXC91231_PIN_AP_GPIO_AP_C9 = IOMUX_PIN(0, 2, 9, 19, 0, 9),
+ MXC91231_PIN_AP_GPIO_AP_C10 = IOMUX_PIN(0, 2, 10, 19, 1, 9),
+ MXC91231_PIN_AP_GPIO_AP_C11 = IOMUX_PIN(0, 2, 11, 19, 2, 9),
+ MXC91231_PIN_AP_GPIO_AP_C12 = IOMUX_PIN(0, 2, 12, 19, 3, 9),
+ MXC91231_PIN_AP_GPIO_AP_C13 = IOMUX_PIN(0, 2, 13, 20, 0, 28),
+ MXC91231_PIN_AP_GPIO_AP_C14 = IOMUX_PIN(0, 2, 14, 20, 1, 28),
+ MXC91231_PIN_AP_GPIO_AP_C15 = IOMUX_PIN(0, 2, 15, 20, 2, 9),
+ MXC91231_PIN_AP_GPIO_AP_C16 = IOMUX_PIN(0, 2, 16, 20, 3, 9),
+ MXC91231_PIN_AP_GPIO_AP_C17 = IOMUX_PIN(0, 2, 17, 21, 0, 9),
+ MXC91231_PIN_AP_ED_INT0 = IOMUX_PIN(0, 2, 18, 21, 1, 22),
+ MXC91231_PIN_AP_ED_INT1 = IOMUX_PIN(0, 2, 19, 21, 2, 22),
+ MXC91231_PIN_AP_ED_INT2 = IOMUX_PIN(0, 2, 20, 21, 3, 22),
+ MXC91231_PIN_AP_ED_INT3 = IOMUX_PIN(0, 2, 21, 22, 0, 22),
+ MXC91231_PIN_AP_ED_INT4 = IOMUX_PIN(0, 2, 22, 22, 1, 23),
+ MXC91231_PIN_AP_ED_INT5 = IOMUX_PIN(0, 2, 23, 22, 2, 23),
+ MXC91231_PIN_AP_ED_INT6 = IOMUX_PIN(0, 2, 24, 22, 3, 23),
+ MXC91231_PIN_AP_ED_INT7 = IOMUX_PIN(0, 2, 25, 23, 0, 23),
+ MXC91231_PIN_AP_U2_DSR_B = IOMUX_PIN(0, 2, 26, 23, 1, 28),
+ MXC91231_PIN_AP_U2_RI_B = IOMUX_PIN(0, 2, 27, 23, 2, 28),
+ MXC91231_PIN_AP_U2_CTS_B = IOMUX_PIN(0, 2, 28, 23, 3, 28),
+ MXC91231_PIN_AP_U2_DTR_B = IOMUX_PIN(0, 2, 29, 24, 0, 28),
+ MXC91231_PIN_AP_KPROW0 = IOMUX_PIN(0, 7, 0, 24, 1, 10),
+ MXC91231_PIN_AP_KPROW1 = IOMUX_PIN(0, 1, 15, 24, 2, 10),
+ MXC91231_PIN_AP_KPROW2 = IOMUX_PIN(0, 7, 0, 24, 3, 10),
+ MXC91231_PIN_AP_KPROW3 = IOMUX_PIN(0, 7, 0, 25, 0, 10),
+ MXC91231_PIN_AP_KPCOL0 = IOMUX_PIN(0, 7, 0, 25, 1, 11),
+ MXC91231_PIN_AP_KPCOL1 = IOMUX_PIN(0, 7, 0, 25, 2, 11),
+ MXC91231_PIN_AP_KPCOL2 = IOMUX_PIN(0, 7, 0, 25, 3, 11),
+
+ /* Shared pins */
+ MXC91231_PIN_SP_U3_TXD = IOMUX_PIN(1, 3, 0, 0, 0, 28),
+ MXC91231_PIN_SP_U3_RXD = IOMUX_PIN(1, 3, 1, 0, 1, 28),
+ MXC91231_PIN_SP_U3_RTS_B = IOMUX_PIN(1, 3, 2, 0, 2, 28),
+ MXC91231_PIN_SP_U3_CTS_B = IOMUX_PIN(1, 3, 3, 0, 3, 28),
+ MXC91231_PIN_SP_USB_TXOE_B = IOMUX_PIN(1, 3, 4, 1, 0, 28),
+ MXC91231_PIN_SP_USB_DAT_VP = IOMUX_PIN(1, 3, 5, 1, 1, 28),
+ MXC91231_PIN_SP_USB_SE0_VM = IOMUX_PIN(1, 3, 6, 1, 2, 28),
+ MXC91231_PIN_SP_USB_RXD = IOMUX_PIN(1, 3, 7, 1, 3, 28),
+ MXC91231_PIN_SP_UH2_TXOE_B = IOMUX_PIN(1, 3, 8, 2, 0, 28),
+ MXC91231_PIN_SP_UH2_SPEED = IOMUX_PIN(1, 3, 9, 2, 1, 28),
+ MXC91231_PIN_SP_UH2_SUSPEN = IOMUX_PIN(1, 3, 10, 2, 2, 28),
+ MXC91231_PIN_SP_UH2_TXDP = IOMUX_PIN(1, 3, 11, 2, 3, 28),
+ MXC91231_PIN_SP_UH2_RXDP = IOMUX_PIN(1, 3, 12, 3, 0, 28),
+ MXC91231_PIN_SP_UH2_RXDM = IOMUX_PIN(1, 3, 13, 3, 1, 28),
+ MXC91231_PIN_SP_UH2_OVR = IOMUX_PIN(1, 3, 14, 3, 2, 28),
+ MXC91231_PIN_SP_UH2_PWR = IOMUX_PIN(1, 3, 15, 3, 3, 28),
+ MXC91231_PIN_SP_SD1_DAT0 = IOMUX_PIN(1, 3, 16, 4, 0, 25),
+ MXC91231_PIN_SP_SD1_DAT1 = IOMUX_PIN(1, 3, 17, 4, 1, 25),
+ MXC91231_PIN_SP_SD1_DAT2 = IOMUX_PIN(1, 3, 18, 4, 2, 25),
+ MXC91231_PIN_SP_SD1_DAT3 = IOMUX_PIN(1, 3, 19, 4, 3, 25),
+ MXC91231_PIN_SP_SD1_CMD = IOMUX_PIN(1, 3, 20, 5, 0, 25),
+ MXC91231_PIN_SP_SD1_CLK = IOMUX_PIN(1, 3, 21, 5, 1, 25),
+ MXC91231_PIN_SP_SD2_DAT0 = IOMUX_PIN(1, 3, 22, 5, 2, 26),
+ MXC91231_PIN_SP_SD2_DAT1 = IOMUX_PIN(1, 3, 23, 5, 3, 26),
+ MXC91231_PIN_SP_SD2_DAT2 = IOMUX_PIN(1, 3, 24, 6, 0, 26),
+ MXC91231_PIN_SP_SD2_DAT3 = IOMUX_PIN(1, 3, 25, 6, 1, 26),
+ MXC91231_PIN_SP_GPIO_SP_A26 = IOMUX_PIN(1, 3, 26, 6, 2, 28),
+ MXC91231_PIN_SP_SPI1_CLK = IOMUX_PIN(1, 3, 27, 6, 3, 13),
+ MXC91231_PIN_SP_SPI1_MOSI = IOMUX_PIN(1, 3, 28, 7, 0, 13),
+ MXC91231_PIN_SP_SPI1_MISO = IOMUX_PIN(1, 3, 29, 7, 1, 13),
+ MXC91231_PIN_SP_SPI1_SS0 = IOMUX_PIN(1, 3, 30, 7, 2, 13),
+ MXC91231_PIN_SP_SPI1_SS1 = IOMUX_PIN(1, 3, 31, 7, 3, 13),
+ MXC91231_PIN_SP_SD2_CMD = IOMUX_PIN(1, 7, 0, 8, 0, 26),
+ MXC91231_PIN_SP_SD2_CLK = IOMUX_PIN(1, 7, 0, 8, 1, 26),
+ MXC91231_PIN_SP_SIM1_RST_B = IOMUX_PIN(1, 2, 30, 8, 2, 28),
+ MXC91231_PIN_SP_SIM1_SVEN = IOMUX_PIN(1, 7, 0, 8, 3, 28),
+ MXC91231_PIN_SP_SIM1_CLK = IOMUX_PIN(1, 7, 0, 9, 0, 28),
+ MXC91231_PIN_SP_SIM1_TRXD = IOMUX_PIN(1, 7, 0, 9, 1, 28),
+ MXC91231_PIN_SP_SIM1_PD = IOMUX_PIN(1, 2, 31, 9, 2, 28),
+ MXC91231_PIN_SP_UH2_TXDM = IOMUX_PIN(1, 7, 0, 9, 3, 28),
+ MXC91231_PIN_SP_UH2_RXD = IOMUX_PIN(1, 7, 0, 10, 0, 28),
+};
+
+#define PIN_AP_MAX (104)
+#define PIN_SP_MAX (41)
+
+#define PIN_MAX (PIN_AP_MAX + PIN_SP_MAX)
+
+/*
+ * Convenience values for use with mxc_iomux_mode()
+ *
+ * Format here is MXC91231_PIN_(pin name)__(function)
+ */
+
+#define MXC91231_PIN_SP_USB_DAT_VP__USB_DAT_VP \
+ IOMUX_MODE(MXC91231_PIN_SP_USB_DAT_VP, IOMUX_CONFIG_FUNC)
+#define MXC91231_PIN_SP_USB_SE0_VM__USB_SE0_VM \
+ IOMUX_MODE(MXC91231_PIN_SP_USB_SE0_VM, IOMUX_CONFIG_FUNC)
+#define MXC91231_PIN_SP_USB_DAT_VP__RXD2 \
+ IOMUX_MODE(MXC91231_PIN_SP_USB_DAT_VP, IOMUX_CONFIG_ALT1)
+#define MXC91231_PIN_SP_USB_SE0_VM__TXD2 \
+ IOMUX_MODE(MXC91231_PIN_SP_USB_SE0_VM, IOMUX_CONFIG_ALT1)
+
+
+#endif /* __MACH_IOMUX_MXC91231_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/iomux-v3.h b/arch/arm/plat-mxc/include/mach/iomux-v3.h
index 7cd84547658..a0fa4026546 100644
--- a/arch/arm/plat-mxc/include/mach/iomux-v3.h
+++ b/arch/arm/plat-mxc/include/mach/iomux-v3.h
@@ -68,28 +68,24 @@ struct pad_desc {
/*
* Use to set PAD control
*/
-#define PAD_CTL_DRIVE_VOLTAGE_3_3_V 0
-#define PAD_CTL_DRIVE_VOLTAGE_1_8_V 1
-#define PAD_CTL_NO_HYSTERESIS 0
-#define PAD_CTL_HYSTERESIS 1
+#define PAD_CTL_DVS (1 << 13)
+#define PAD_CTL_HYS (1 << 8)
-#define PAD_CTL_PULL_DISABLED 0x0
-#define PAD_CTL_PULL_KEEPER 0xa
-#define PAD_CTL_PULL_DOWN_100K 0xc
-#define PAD_CTL_PULL_UP_47K 0xd
-#define PAD_CTL_PULL_UP_100K 0xe
-#define PAD_CTL_PULL_UP_22K 0xf
+#define PAD_CTL_PKE (1 << 7)
+#define PAD_CTL_PUE (1 << 6)
+#define PAD_CTL_PUS_100K_DOWN (0 << 4)
+#define PAD_CTL_PUS_47K_UP (1 << 4)
+#define PAD_CTL_PUS_100K_UP (2 << 4)
+#define PAD_CTL_PUS_22K_UP (3 << 4)
-#define PAD_CTL_OUTPUT_CMOS 0
-#define PAD_CTL_OUTPUT_OPEN_DRAIN 1
+#define PAD_CTL_ODE (1 << 3)
-#define PAD_CTL_DRIVE_STRENGTH_NORM 0
-#define PAD_CTL_DRIVE_STRENGTH_HIGH 1
-#define PAD_CTL_DRIVE_STRENGTH_MAX 2
+#define PAD_CTL_DSE_STANDARD (0 << 1)
+#define PAD_CTL_DSE_HIGH (1 << 1)
+#define PAD_CTL_DSE_MAX (2 << 1)
-#define PAD_CTL_SLEW_RATE_SLOW 0
-#define PAD_CTL_SLEW_RATE_FAST 1
+#define PAD_CTL_SRE_FAST (1 << 0)
/*
* setups a single pad:
@@ -117,5 +113,10 @@ void mxc_iomux_v3_release_pad(struct pad_desc *pad);
*/
void mxc_iomux_v3_release_multiple_pads(struct pad_desc *pad_list, int count);
+/*
+ * Initialise the iomux controller
+ */
+void mxc_iomux_v3_init(void __iomem *iomux_v3_base);
+
#endif /* __MACH_IOMUX_V3_H__*/
diff --git a/arch/arm/plat-mxc/include/mach/iomux.h b/arch/arm/plat-mxc/include/mach/iomux.h
index 171f8adc110..6d49f8ae325 100644
--- a/arch/arm/plat-mxc/include/mach/iomux.h
+++ b/arch/arm/plat-mxc/include/mach/iomux.h
@@ -49,6 +49,9 @@
#ifdef CONFIG_ARCH_MX2
# define GPIO_PORT_MAX 5
#endif
+#ifdef CONFIG_ARCH_MX25
+# define GPIO_PORT_MAX 3
+#endif
#ifndef GPIO_PORT_MAX
# error "GPIO config port count unknown!"
@@ -107,6 +110,9 @@
#include <mach/iomux-mx27.h>
#endif
#endif
+#ifdef CONFIG_ARCH_MX25
+#include <mach/iomux-mx25.h>
+#endif
/* decode irq number to use with IMR(x), ISR(x) and friends */
diff --git a/arch/arm/plat-mxc/include/mach/irqs.h b/arch/arm/plat-mxc/include/mach/irqs.h
index 518a36504b8..ead9d592168 100644
--- a/arch/arm/plat-mxc/include/mach/irqs.h
+++ b/arch/arm/plat-mxc/include/mach/irqs.h
@@ -24,6 +24,10 @@
#define MXC_GPIO_IRQS (32 * 6)
#elif defined CONFIG_ARCH_MX3
#define MXC_GPIO_IRQS (32 * 3)
+#elif defined CONFIG_ARCH_MX25
+#define MXC_GPIO_IRQS (32 * 4)
+#elif defined CONFIG_ARCH_MXC91231
+#define MXC_GPIO_IRQS (32 * 4)
#endif
/*
diff --git a/arch/arm/plat-mxc/include/mach/memory.h b/arch/arm/plat-mxc/include/mach/memory.h
index 6065e00176e..d3afafdcc0e 100644
--- a/arch/arm/plat-mxc/include/mach/memory.h
+++ b/arch/arm/plat-mxc/include/mach/memory.h
@@ -22,6 +22,10 @@
#endif
#elif defined CONFIG_ARCH_MX3
#define PHYS_OFFSET UL(0x80000000)
+#elif defined CONFIG_ARCH_MX25
+#define PHYS_OFFSET UL(0x80000000)
+#elif defined CONFIG_ARCH_MXC91231
+#define PHYS_OFFSET UL(0x90000000)
#endif
#if defined(CONFIG_MX1_VIDEO)
diff --git a/arch/arm/plat-mxc/include/mach/mx1.h b/arch/arm/plat-mxc/include/mach/mx1.h
index 1000bf330bc..1b2890a5c45 100644
--- a/arch/arm/plat-mxc/include/mach/mx1.h
+++ b/arch/arm/plat-mxc/include/mach/mx1.h
@@ -12,10 +12,6 @@
#ifndef __ASM_ARCH_MXC_MX1_H__
#define __ASM_ARCH_MXC_MX1_H__
-#ifndef __ASM_ARCH_MXC_HARDWARE_H__
-#error "Do not include directly."
-#endif
-
#include <mach/vmalloc.h>
/*
@@ -138,20 +134,6 @@
#define GPIO_INT_PORTD 62
#define WDT_INT 63
-/* gpio and gpio based interrupt handling */
-#define GPIO_DR 0x1C
-#define GPIO_GDIR 0x00
-#define GPIO_PSR 0x24
-#define GPIO_ICR1 0x28
-#define GPIO_ICR2 0x2C
-#define GPIO_IMR 0x30
-#define GPIO_ISR 0x34
-#define GPIO_INT_LOW_LEV 0x3
-#define GPIO_INT_HIGH_LEV 0x2
-#define GPIO_INT_RISE_EDGE 0x0
-#define GPIO_INT_FALL_EDGE 0x1
-#define GPIO_INT_NONE 0x4
-
/* DMA */
#define DMA_REQ_UART3_T 2
#define DMA_REQ_UART3_R 3
@@ -179,8 +161,4 @@
#define DMA_REQ_UART1_T 30
#define DMA_REQ_UART1_R 31
-/* mandatory for CONFIG_DEBUG_LL */
-#define MXC_LL_UART_PADDR UART1_BASE_ADDR
-#define MXC_LL_UART_VADDR IO_ADDRESS(UART1_BASE_ADDR)
-
#endif /* __ASM_ARCH_MXC_MX1_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mx21.h b/arch/arm/plat-mxc/include/mach/mx21.h
index 8b070a041a9..21112c695ec 100644
--- a/arch/arm/plat-mxc/include/mach/mx21.h
+++ b/arch/arm/plat-mxc/include/mach/mx21.h
@@ -25,11 +25,6 @@
#ifndef __ASM_ARCH_MXC_MX21_H__
#define __ASM_ARCH_MXC_MX21_H__
-#ifndef __ASM_ARCH_MXC_HARDWARE_H__
-#error "Do not include directly."
-#endif
-
-
/* Memory regions and CS */
#define SDRAM_BASE_ADDR 0xC0000000
#define CSD1_BASE_ADDR 0xC4000000
diff --git a/arch/arm/plat-mxc/include/mach/mx25.h b/arch/arm/plat-mxc/include/mach/mx25.h
new file mode 100644
index 00000000000..ec64bd9a8ab
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mx25.h
@@ -0,0 +1,44 @@
+#ifndef __MACH_MX25_H__
+#define __MACH_MX25_H__
+
+#define MX25_AIPS1_BASE_ADDR 0x43F00000
+#define MX25_AIPS1_BASE_ADDR_VIRT 0xFC000000
+#define MX25_AIPS1_SIZE SZ_1M
+#define MX25_AIPS2_BASE_ADDR 0x53F00000
+#define MX25_AIPS2_BASE_ADDR_VIRT 0xFC200000
+#define MX25_AIPS2_SIZE SZ_1M
+#define MX25_AVIC_BASE_ADDR 0x68000000
+#define MX25_AVIC_BASE_ADDR_VIRT 0xFC400000
+#define MX25_AVIC_SIZE SZ_1M
+
+#define MX25_IOMUXC_BASE_ADDR (MX25_AIPS1_BASE_ADDR + 0xac000)
+
+#define MX25_CRM_BASE_ADDR (MX25_AIPS2_BASE_ADDR + 0x80000)
+#define MX25_GPT1_BASE_ADDR (MX25_AIPS2_BASE_ADDR + 0x90000)
+#define MX25_WDOG_BASE_ADDR (MX25_AIPS2_BASE_ADDR + 0xdc000)
+
+#define MX25_GPIO1_BASE_ADDR_VIRT (MX25_AIPS2_BASE_ADDR_VIRT + 0xcc000)
+#define MX25_GPIO2_BASE_ADDR_VIRT (MX25_AIPS2_BASE_ADDR_VIRT + 0xd0000)
+#define MX25_GPIO3_BASE_ADDR_VIRT (MX25_AIPS2_BASE_ADDR_VIRT + 0xa4000)
+#define MX25_GPIO4_BASE_ADDR_VIRT (MX25_AIPS2_BASE_ADDR_VIRT + 0x9c000)
+
+#define MX25_AIPS1_IO_ADDRESS(x) \
+ (((x) - MX25_AIPS1_BASE_ADDR) + MX25_AIPS1_BASE_ADDR_VIRT)
+#define MX25_AIPS2_IO_ADDRESS(x) \
+ (((x) - MX25_AIPS2_BASE_ADDR) + MX25_AIPS2_BASE_ADDR_VIRT)
+#define MX25_AVIC_IO_ADDRESS(x) \
+ (((x) - MX25_AVIC_BASE_ADDR) + MX25_AVIC_BASE_ADDR_VIRT)
+
+#define __in_range(addr, name) ((addr) >= name##_BASE_ADDR && (addr) < name##_BASE_ADDR + name##_SIZE)
+
+#define MX25_IO_ADDRESS(x) \
+ (void __force __iomem *) \
+ (__in_range(x, MX25_AIPS1) ? MX25_AIPS1_IO_ADDRESS(x) : \
+ __in_range(x, MX25_AIPS2) ? MX25_AIPS2_IO_ADDRESS(x) : \
+ __in_range(x, MX25_AVIC) ? MX25_AVIC_IO_ADDRESS(x) : \
+ 0xDEADBEEF)
+
+#define UART1_BASE_ADDR 0x43f90000
+#define UART2_BASE_ADDR 0x43f94000
+
+#endif /* __MACH_MX25_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mx27.h b/arch/arm/plat-mxc/include/mach/mx27.h
index 6e93f2c0b7b..dc3ad9aa952 100644
--- a/arch/arm/plat-mxc/include/mach/mx27.h
+++ b/arch/arm/plat-mxc/include/mach/mx27.h
@@ -24,10 +24,6 @@
#ifndef __ASM_ARCH_MXC_MX27_H__
#define __ASM_ARCH_MXC_MX27_H__
-#ifndef __ASM_ARCH_MXC_HARDWARE_H__
-#error "Do not include directly."
-#endif
-
/* IRAM */
#define IRAM_BASE_ADDR 0xFFFF4C00 /* internal ram */
@@ -120,7 +116,4 @@ extern int mx27_revision(void);
/* Mandatory defines used globally */
-/* this CPU supports up to 192 GPIOs (don't forget the baseboard!) */
-#define ARCH_NR_GPIOS (192 + 16)
-
#endif /* __ASM_ARCH_MXC_MX27_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mx2x.h b/arch/arm/plat-mxc/include/mach/mx2x.h
index fc40d3ab8c5..db5d921e0fe 100644
--- a/arch/arm/plat-mxc/include/mach/mx2x.h
+++ b/arch/arm/plat-mxc/include/mach/mx2x.h
@@ -23,10 +23,6 @@
#ifndef __ASM_ARCH_MXC_MX2x_H__
#define __ASM_ARCH_MXC_MX2x_H__
-#ifndef __ASM_ARCH_MXC_HARDWARE_H__
-#error "Do not include directly."
-#endif
-
/* The following addresses are common between i.MX21 and i.MX27 */
/* Register offests */
@@ -154,20 +150,6 @@
#define MXC_INT_GPIO 8
#define MXC_INT_CSPI3 6
-/* gpio and gpio based interrupt handling */
-#define GPIO_DR 0x1C
-#define GPIO_GDIR 0x00
-#define GPIO_PSR 0x24
-#define GPIO_ICR1 0x28
-#define GPIO_ICR2 0x2C
-#define GPIO_IMR 0x30
-#define GPIO_ISR 0x34
-#define GPIO_INT_LOW_LEV 0x3
-#define GPIO_INT_HIGH_LEV 0x2
-#define GPIO_INT_RISE_EDGE 0x0
-#define GPIO_INT_FALL_EDGE 0x1
-#define GPIO_INT_NONE 0x4
-
/* fixed DMA request numbers */
#define DMA_REQ_CSI_RX 31
#define DMA_REQ_CSI_STAT 30
diff --git a/arch/arm/plat-mxc/include/mach/mx31.h b/arch/arm/plat-mxc/include/mach/mx31.h
index 0b06941b613..14ac0dcc82f 100644
--- a/arch/arm/plat-mxc/include/mach/mx31.h
+++ b/arch/arm/plat-mxc/include/mach/mx31.h
@@ -4,7 +4,7 @@
#define MX31_IRAM_BASE_ADDR 0x1FFC0000 /* internal ram */
#define MX31_IRAM_SIZE SZ_16K
-#define OTG_BASE_ADDR (AIPS1_BASE_ADDR + 0x00088000)
+#define MX31_OTG_BASE_ADDR (AIPS1_BASE_ADDR + 0x00088000)
#define ATA_BASE_ADDR (AIPS1_BASE_ADDR + 0x0008C000)
#define UART4_BASE_ADDR (AIPS1_BASE_ADDR + 0x000B0000)
#define UART5_BASE_ADDR (AIPS1_BASE_ADDR + 0x000B4000)
diff --git a/arch/arm/plat-mxc/include/mach/mx35.h b/arch/arm/plat-mxc/include/mach/mx35.h
index 6465fefb42e..ab4cfec6c8a 100644
--- a/arch/arm/plat-mxc/include/mach/mx35.h
+++ b/arch/arm/plat-mxc/include/mach/mx35.h
@@ -5,6 +5,7 @@
#define MX35_IRAM_SIZE SZ_128K
#define MXC_FEC_BASE_ADDR 0x50038000
+#define MX35_OTG_BASE_ADDR 0x53ff4000
#define MX35_NFC_BASE_ADDR 0xBB000000
/*
diff --git a/arch/arm/plat-mxc/include/mach/mx3x.h b/arch/arm/plat-mxc/include/mach/mx3x.h
index b559a4bb576..009f4440276 100644
--- a/arch/arm/plat-mxc/include/mach/mx3x.h
+++ b/arch/arm/plat-mxc/include/mach/mx3x.h
@@ -11,10 +11,6 @@
#ifndef __ASM_ARCH_MXC_MX31_H__
#define __ASM_ARCH_MXC_MX31_H__
-#ifndef __ASM_ARCH_MXC_HARDWARE_H__
-#error "Do not include directly."
-#endif
-
/*
* MX31 memory map:
*
@@ -263,25 +259,8 @@
#define SYSTEM_REV_MIN CHIP_REV_1_0
#define SYSTEM_REV_NUM 3
-/* gpio and gpio based interrupt handling */
-#define GPIO_DR 0x00
-#define GPIO_GDIR 0x04
-#define GPIO_PSR 0x08
-#define GPIO_ICR1 0x0C
-#define GPIO_ICR2 0x10
-#define GPIO_IMR 0x14
-#define GPIO_ISR 0x18
-#define GPIO_INT_LOW_LEV 0x0
-#define GPIO_INT_HIGH_LEV 0x1
-#define GPIO_INT_RISE_EDGE 0x2
-#define GPIO_INT_FALL_EDGE 0x3
-#define GPIO_INT_NONE 0x4
-
/* Mandatory defines used globally */
-/* this CPU supports up to 96 GPIOs */
-#define ARCH_NR_GPIOS 96
-
#if !defined(__ASSEMBLY__) && !defined(__MXC_BOOT_UNCOMPRESS)
extern unsigned int system_rev;
diff --git a/arch/arm/plat-mxc/include/mach/mxc.h b/arch/arm/plat-mxc/include/mach/mxc.h
index 5fa2a07f4ea..51990536b84 100644
--- a/arch/arm/plat-mxc/include/mach/mxc.h
+++ b/arch/arm/plat-mxc/include/mach/mxc.h
@@ -26,9 +26,11 @@
#define MXC_CPU_MX1 1
#define MXC_CPU_MX21 21
+#define MXC_CPU_MX25 25
#define MXC_CPU_MX27 27
#define MXC_CPU_MX31 31
#define MXC_CPU_MX35 35
+#define MXC_CPU_MXC91231 91231
#ifndef __ASSEMBLY__
extern unsigned int __mxc_cpu_type;
@@ -58,6 +60,18 @@ extern unsigned int __mxc_cpu_type;
# define cpu_is_mx21() (0)
#endif
+#ifdef CONFIG_ARCH_MX25
+# ifdef mxc_cpu_type
+# undef mxc_cpu_type
+# define mxc_cpu_type __mxc_cpu_type
+# else
+# define mxc_cpu_type MXC_CPU_MX25
+# endif
+# define cpu_is_mx25() (mxc_cpu_type == MXC_CPU_MX25)
+#else
+# define cpu_is_mx25() (0)
+#endif
+
#ifdef CONFIG_MACH_MX27
# ifdef mxc_cpu_type
# undef mxc_cpu_type
@@ -94,13 +108,25 @@ extern unsigned int __mxc_cpu_type;
# define cpu_is_mx35() (0)
#endif
+#ifdef CONFIG_ARCH_MXC91231
+# ifdef mxc_cpu_type
+# undef mxc_cpu_type
+# define mxc_cpu_type __mxc_cpu_type
+# else
+# define mxc_cpu_type MXC_CPU_MXC91231
+# endif
+# define cpu_is_mxc91231() (mxc_cpu_type == MXC_CPU_MXC91231)
+#else
+# define cpu_is_mxc91231() (0)
+#endif
+
#if defined(CONFIG_ARCH_MX3) || defined(CONFIG_ARCH_MX2)
#define CSCR_U(n) (IO_ADDRESS(WEIM_BASE_ADDR) + n * 0x10)
#define CSCR_L(n) (IO_ADDRESS(WEIM_BASE_ADDR) + n * 0x10 + 0x4)
#define CSCR_A(n) (IO_ADDRESS(WEIM_BASE_ADDR) + n * 0x10 + 0x8)
#endif
-#define cpu_is_mx3() (cpu_is_mx31() || cpu_is_mx35())
+#define cpu_is_mx3() (cpu_is_mx31() || cpu_is_mx35() || cpu_is_mxc91231())
#define cpu_is_mx2() (cpu_is_mx21() || cpu_is_mx27())
#endif /* __ASM_ARCH_MXC_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/mxc91231.h b/arch/arm/plat-mxc/include/mach/mxc91231.h
new file mode 100644
index 00000000000..81484d1ef23
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/mxc91231.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright 2004-2006 Freescale Semiconductor, Inc. All Rights Reserved.
+ * - Platform specific register memory map
+ *
+ * Copyright 2005-2007 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __MACH_MXC91231_H__
+#define __MACH_MXC91231_H__
+
+/*
+ * L2CC
+ */
+#define MXC91231_L2CC_BASE_ADDR 0x30000000
+#define MXC91231_L2CC_BASE_ADDR_VIRT 0xF9000000
+#define MXC91231_L2CC_SIZE SZ_64K
+
+/*
+ * AIPS 1
+ */
+#define MXC91231_AIPS1_BASE_ADDR 0x43F00000
+#define MXC91231_AIPS1_BASE_ADDR_VIRT 0xFC000000
+#define MXC91231_AIPS1_SIZE SZ_1M
+
+#define MXC91231_AIPS1_CTRL_BASE_ADDR MXC91231_AIPS1_BASE_ADDR
+#define MXC91231_MAX_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0x04000)
+#define MXC91231_EVTMON_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0x08000)
+#define MXC91231_CLKCTL_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0x0C000)
+#define MXC91231_ETB_SLOT4_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0x10000)
+#define MXC91231_ETB_SLOT5_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0x14000)
+#define MXC91231_ECT_CTIO_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0x18000)
+#define MXC91231_I2C_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0x80000)
+#define MXC91231_MU_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0x88000)
+#define MXC91231_UART1_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0x90000)
+#define MXC91231_UART2_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0x94000)
+#define MXC91231_DSM_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0x98000)
+#define MXC91231_OWIRE_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0x9C000)
+#define MXC91231_SSI1_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0xA0000)
+#define MXC91231_KPP_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0xA8000)
+#define MXC91231_IOMUX_AP_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0xAC000)
+#define MXC91231_CTI_AP_BASE_ADDR (MXC91231_AIPS1_BASE_ADDR + 0xB8000)
+
+/*
+ * AIPS 2
+ */
+#define MXC91231_AIPS2_BASE_ADDR 0x53F00000
+#define MXC91231_AIPS2_BASE_ADDR_VIRT 0xFC100000
+#define MXC91231_AIPS2_SIZE SZ_1M
+
+#define MXC91231_GEMK_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0x8C000)
+#define MXC91231_GPT1_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0x90000)
+#define MXC91231_EPIT1_AP_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0x94000)
+#define MXC91231_SCC_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xAC000)
+#define MXC91231_RNGA_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xB0000)
+#define MXC91231_IPU_CTRL_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xC0000)
+#define MXC91231_AUDMUX_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xC4000)
+#define MXC91231_EDIO_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xC8000)
+#define MXC91231_GPIO1_AP_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xCC000)
+#define MXC91231_GPIO2_AP_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xD0000)
+#define MXC91231_SDMA_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xD4000)
+#define MXC91231_RTC_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xD8000)
+#define MXC91231_WDOG1_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xDC000)
+#define MXC91231_PWM_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xE0000)
+#define MXC91231_GPIO3_AP_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xE4000)
+#define MXC91231_WDOG2_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xE8000)
+#define MXC91231_RTIC_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xEC000)
+#define MXC91231_LPMC_BASE_ADDR (MXC91231_AIPS2_BASE_ADDR + 0xF0000)
+
+/*
+ * SPBA global module 0
+ */
+#define MXC91231_SPBA0_BASE_ADDR 0x50000000
+#define MXC91231_SPBA0_BASE_ADDR_VIRT 0xFC200000
+#define MXC91231_SPBA0_SIZE SZ_1M
+
+#define MXC91231_MMC_SDHC1_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x04000)
+#define MXC91231_MMC_SDHC2_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x08000)
+#define MXC91231_UART3_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x0C000)
+#define MXC91231_CSPI2_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x10000)
+#define MXC91231_SSI2_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x14000)
+#define MXC91231_SIM_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x18000)
+#define MXC91231_IIM_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x1C000)
+#define MXC91231_CTI_SDMA_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x20000)
+#define MXC91231_USBOTG_CTRL_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x24000)
+#define MXC91231_USBOTG_DATA_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x28000)
+#define MXC91231_CSPI1_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x30000)
+#define MXC91231_SPBA_CTRL_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x3C000)
+#define MXC91231_IOMUX_COM_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x40000)
+#define MXC91231_CRM_COM_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x44000)
+#define MXC91231_CRM_AP_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x48000)
+#define MXC91231_PLL0_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x4C000)
+#define MXC91231_PLL1_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x50000)
+#define MXC91231_PLL2_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x54000)
+#define MXC91231_GPIO4_SH_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x58000)
+#define MXC91231_HAC_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x5C000)
+#define MXC91231_SAHARA_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x5C000)
+#define MXC91231_PLL3_BASE_ADDR (MXC91231_SPBA0_BASE_ADDR + 0x60000)
+
+/*
+ * SPBA global module 1
+ */
+#define MXC91231_SPBA1_BASE_ADDR 0x52000000
+#define MXC91231_SPBA1_BASE_ADDR_VIRT 0xFC300000
+#define MXC91231_SPBA1_SIZE SZ_1M
+
+#define MXC91231_MQSPI_BASE_ADDR (MXC91231_SPBA1_BASE_ADDR + 0x34000)
+#define MXC91231_EL1T_BASE_ADDR (MXC91231_SPBA1_BASE_ADDR + 0x38000)
+
+/*!
+ * Defines for SPBA modules
+ */
+#define MXC91231_SPBA_SDHC1 0x04
+#define MXC91231_SPBA_SDHC2 0x08
+#define MXC91231_SPBA_UART3 0x0C
+#define MXC91231_SPBA_CSPI2 0x10
+#define MXC91231_SPBA_SSI2 0x14
+#define MXC91231_SPBA_SIM 0x18
+#define MXC91231_SPBA_IIM 0x1C
+#define MXC91231_SPBA_CTI_SDMA 0x20
+#define MXC91231_SPBA_USBOTG_CTRL_REGS 0x24
+#define MXC91231_SPBA_USBOTG_DATA_REGS 0x28
+#define MXC91231_SPBA_CSPI1 0x30
+#define MXC91231_SPBA_MQSPI 0x34
+#define MXC91231_SPBA_EL1T 0x38
+#define MXC91231_SPBA_IOMUX 0x40
+#define MXC91231_SPBA_CRM_COM 0x44
+#define MXC91231_SPBA_CRM_AP 0x48
+#define MXC91231_SPBA_PLL0 0x4C
+#define MXC91231_SPBA_PLL1 0x50
+#define MXC91231_SPBA_PLL2 0x54
+#define MXC91231_SPBA_GPIO4 0x58
+#define MXC91231_SPBA_SAHARA 0x5C
+
+/*
+ * ROMP and AVIC
+ */
+#define MXC91231_ROMP_BASE_ADDR 0x60000000
+#define MXC91231_ROMP_BASE_ADDR_VIRT 0xFC400000
+#define MXC91231_ROMP_SIZE SZ_64K
+
+#define MXC91231_AVIC_BASE_ADDR 0x68000000
+#define MXC91231_AVIC_BASE_ADDR_VIRT 0xFC410000
+#define MXC91231_AVIC_SIZE SZ_64K
+
+/*
+ * NAND, SDRAM, WEIM, M3IF, EMI controllers
+ */
+#define MXC91231_X_MEMC_BASE_ADDR 0xB8000000
+#define MXC91231_X_MEMC_BASE_ADDR_VIRT 0xFC420000
+#define MXC91231_X_MEMC_SIZE SZ_64K
+
+#define MXC91231_NFC_BASE_ADDR (MXC91231_X_MEMC_BASE_ADDR + 0x0000)
+#define MXC91231_ESDCTL_BASE_ADDR (MXC91231_X_MEMC_BASE_ADDR + 0x1000)
+#define MXC91231_WEIM_BASE_ADDR (MXC91231_X_MEMC_BASE_ADDR + 0x2000)
+#define MXC91231_M3IF_BASE_ADDR (MXC91231_X_MEMC_BASE_ADDR + 0x3000)
+#define MXC91231_EMI_CTL_BASE_ADDR (MXC91231_X_MEMC_BASE_ADDR + 0x4000)
+
+/*
+ * Memory regions and CS
+ * CPLD is connected on CS4
+ * CS5 is TP1021 or it is not connected
+ * */
+#define MXC91231_FB_RAM_BASE_ADDR 0x78000000
+#define MXC91231_FB_RAM_SIZE SZ_256K
+#define MXC91231_CSD0_BASE_ADDR 0x80000000
+#define MXC91231_CSD1_BASE_ADDR 0x90000000
+#define MXC91231_CS0_BASE_ADDR 0xA0000000
+#define MXC91231_CS1_BASE_ADDR 0xA8000000
+#define MXC91231_CS2_BASE_ADDR 0xB0000000
+#define MXC91231_CS3_BASE_ADDR 0xB2000000
+#define MXC91231_CS4_BASE_ADDR 0xB4000000
+#define MXC91231_CS5_BASE_ADDR 0xB6000000
+
+/* Is given address belongs to the specified memory region? */
+#define ADDRESS_IN_REGION(addr, start, size) \
+ (((addr) >= (start)) && ((addr) < (start)+(size)))
+
+/* Is given address belongs to the specified named `module'? */
+#define MXC91231_IS_MODULE(addr, module) \
+ ADDRESS_IN_REGION(addr, MXC91231_ ## module ## _BASE_ADDR, \
+ MXC91231_ ## module ## _SIZE)
+/*
+ * This macro defines the physical to virtual address mapping for all the
+ * peripheral modules. It is used by passing in the physical address as x
+ * and returning the virtual address. If the physical address is not mapped,
+ * it returns 0xDEADBEEF
+ */
+
+#define MXC91231_IO_ADDRESS(x) \
+ (void __iomem *) \
+ (MXC91231_IS_MODULE(x, L2CC) ? MXC91231_L2CC_IO_ADDRESS(x) : \
+ MXC91231_IS_MODULE(x, AIPS1) ? MXC91231_AIPS1_IO_ADDRESS(x) : \
+ MXC91231_IS_MODULE(x, AIPS2) ? MXC91231_AIPS2_IO_ADDRESS(x) : \
+ MXC91231_IS_MODULE(x, SPBA0) ? MXC91231_SPBA0_IO_ADDRESS(x) : \
+ MXC91231_IS_MODULE(x, SPBA1) ? MXC91231_SPBA1_IO_ADDRESS(x) : \
+ MXC91231_IS_MODULE(x, ROMP) ? MXC91231_ROMP_IO_ADDRESS(x) : \
+ MXC91231_IS_MODULE(x, AVIC) ? MXC91231_AVIC_IO_ADDRESS(x) : \
+ MXC91231_IS_MODULE(x, X_MEMC) ? MXC91231_X_MEMC_IO_ADDRESS(x) : \
+ 0xDEADBEEF)
+
+
+/*
+ * define the address mapping macros: in physical address order
+ */
+#define MXC91231_L2CC_IO_ADDRESS(x) \
+ (((x) - MXC91231_L2CC_BASE_ADDR) + MXC91231_L2CC_BASE_ADDR_VIRT)
+
+#define MXC91231_AIPS1_IO_ADDRESS(x) \
+ (((x) - MXC91231_AIPS1_BASE_ADDR) + MXC91231_AIPS1_BASE_ADDR_VIRT)
+
+#define MXC91231_SPBA0_IO_ADDRESS(x) \
+ (((x) - MXC91231_SPBA0_BASE_ADDR) + MXC91231_SPBA0_BASE_ADDR_VIRT)
+
+#define MXC91231_SPBA1_IO_ADDRESS(x) \
+ (((x) - MXC91231_SPBA1_BASE_ADDR) + MXC91231_SPBA1_BASE_ADDR_VIRT)
+
+#define MXC91231_AIPS2_IO_ADDRESS(x) \
+ (((x) - MXC91231_AIPS2_BASE_ADDR) + MXC91231_AIPS2_BASE_ADDR_VIRT)
+
+#define MXC91231_ROMP_IO_ADDRESS(x) \
+ (((x) - MXC91231_ROMP_BASE_ADDR) + MXC91231_ROMP_BASE_ADDR_VIRT)
+
+#define MXC91231_AVIC_IO_ADDRESS(x) \
+ (((x) - MXC91231_AVIC_BASE_ADDR) + MXC91231_AVIC_BASE_ADDR_VIRT)
+
+#define MXC91231_X_MEMC_IO_ADDRESS(x) \
+ (((x) - MXC91231_X_MEMC_BASE_ADDR) + MXC91231_X_MEMC_BASE_ADDR_VIRT)
+
+/*
+ * Interrupt numbers
+ */
+#define MXC91231_INT_GPIO3 0
+#define MXC91231_INT_EL1T_CI 1
+#define MXC91231_INT_EL1T_RFCI 2
+#define MXC91231_INT_EL1T_RFI 3
+#define MXC91231_INT_EL1T_MCU 4
+#define MXC91231_INT_EL1T_IPI 5
+#define MXC91231_INT_MU_GEN 6
+#define MXC91231_INT_GPIO4 7
+#define MXC91231_INT_MMC_SDHC2 8
+#define MXC91231_INT_MMC_SDHC1 9
+#define MXC91231_INT_I2C 10
+#define MXC91231_INT_SSI2 11
+#define MXC91231_INT_SSI1 12
+#define MXC91231_INT_CSPI2 13
+#define MXC91231_INT_CSPI1 14
+#define MXC91231_INT_RTIC 15
+#define MXC91231_INT_SAHARA 15
+#define MXC91231_INT_HAC 15
+#define MXC91231_INT_UART3_RX 16
+#define MXC91231_INT_UART3_TX 17
+#define MXC91231_INT_UART3_MINT 18
+#define MXC91231_INT_ECT 19
+#define MXC91231_INT_SIM_IPB 20
+#define MXC91231_INT_SIM_DATA 21
+#define MXC91231_INT_RNGA 22
+#define MXC91231_INT_DSM_AP 23
+#define MXC91231_INT_KPP 24
+#define MXC91231_INT_RTC 25
+#define MXC91231_INT_PWM 26
+#define MXC91231_INT_GEMK_AP 27
+#define MXC91231_INT_EPIT 28
+#define MXC91231_INT_GPT 29
+#define MXC91231_INT_UART2_RX 30
+#define MXC91231_INT_UART2_TX 31
+#define MXC91231_INT_UART2_MINT 32
+#define MXC91231_INT_NANDFC 33
+#define MXC91231_INT_SDMA 34
+#define MXC91231_INT_USB_WAKEUP 35
+#define MXC91231_INT_USB_SOF 36
+#define MXC91231_INT_PMU_EVTMON 37
+#define MXC91231_INT_USB_FUNC 38
+#define MXC91231_INT_USB_DMA 39
+#define MXC91231_INT_USB_CTRL 40
+#define MXC91231_INT_IPU_ERR 41
+#define MXC91231_INT_IPU_SYN 42
+#define MXC91231_INT_UART1_RX 43
+#define MXC91231_INT_UART1_TX 44
+#define MXC91231_INT_UART1_MINT 45
+#define MXC91231_INT_IIM 46
+#define MXC91231_INT_MU_RX_OR 47
+#define MXC91231_INT_MU_TX_OR 48
+#define MXC91231_INT_SCC_SCM 49
+#define MXC91231_INT_SCC_SMN 50
+#define MXC91231_INT_GPIO2 51
+#define MXC91231_INT_GPIO1 52
+#define MXC91231_INT_MQSPI1 53
+#define MXC91231_INT_MQSPI2 54
+#define MXC91231_INT_WDOG2 55
+#define MXC91231_INT_EXT_INT7 56
+#define MXC91231_INT_EXT_INT6 57
+#define MXC91231_INT_EXT_INT5 58
+#define MXC91231_INT_EXT_INT4 59
+#define MXC91231_INT_EXT_INT3 60
+#define MXC91231_INT_EXT_INT2 61
+#define MXC91231_INT_EXT_INT1 62
+#define MXC91231_INT_EXT_INT0 63
+
+#define MXC91231_MAX_INT_LINES 63
+#define MXC91231_MAX_EXT_LINES 8
+
+#endif /* __MACH_MXC91231_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/system.h b/arch/arm/plat-mxc/include/mach/system.h
index e56241af870..ef00199568d 100644
--- a/arch/arm/plat-mxc/include/mach/system.h
+++ b/arch/arm/plat-mxc/include/mach/system.h
@@ -21,8 +21,18 @@
#ifndef __ASM_ARCH_MXC_SYSTEM_H__
#define __ASM_ARCH_MXC_SYSTEM_H__
+#include <mach/hardware.h>
+#include <mach/common.h>
+
static inline void arch_idle(void)
{
+#ifdef CONFIG_ARCH_MXC91231
+ if (cpu_is_mxc91231()) {
+ /* Need this to set DSM low-power mode */
+ mxc91231_prepare_idle();
+ }
+#endif
+
cpu_do_idle();
}
diff --git a/arch/arm/plat-mxc/include/mach/timex.h b/arch/arm/plat-mxc/include/mach/timex.h
index 07b4a73c9d2..527a6c24788 100644
--- a/arch/arm/plat-mxc/include/mach/timex.h
+++ b/arch/arm/plat-mxc/include/mach/timex.h
@@ -26,6 +26,10 @@
#define CLOCK_TICK_RATE 13300000
#elif defined CONFIG_ARCH_MX3
#define CLOCK_TICK_RATE 16625000
+#elif defined CONFIG_ARCH_MX25
+#define CLOCK_TICK_RATE 16000000
+#elif defined CONFIG_ARCH_MXC91231
+#define CLOCK_TICK_RATE 13000000
#endif
#endif /* __ASM_ARCH_MXC_TIMEX_H__ */
diff --git a/arch/arm/plat-mxc/include/mach/uncompress.h b/arch/arm/plat-mxc/include/mach/uncompress.h
index de6fe036598..082a3908256 100644
--- a/arch/arm/plat-mxc/include/mach/uncompress.h
+++ b/arch/arm/plat-mxc/include/mach/uncompress.h
@@ -26,8 +26,11 @@
#define __MXC_BOOT_UNCOMPRESS
#include <mach/hardware.h>
+#include <asm/mach-types.h>
-#define UART(x) (*(volatile unsigned long *)(serial_port + (x)))
+static unsigned long uart_base;
+
+#define UART(x) (*(volatile unsigned long *)(uart_base + (x)))
#define USR2 0x98
#define USR2_TXFE (1<<14)
@@ -46,19 +49,10 @@
static void putc(int ch)
{
- static unsigned long serial_port = 0;
-
- if (unlikely(serial_port == 0)) {
- do {
- serial_port = UART1_BASE_ADDR;
- if (UART(UCR1) & UCR1_UARTEN)
- break;
- serial_port = UART2_BASE_ADDR;
- if (UART(UCR1) & UCR1_UARTEN)
- break;
- return;
- } while (0);
- }
+ if (!uart_base)
+ return;
+ if (!(UART(UCR1) & UCR1_UARTEN))
+ return;
while (!(UART(USR2) & USR2_TXFE))
barrier();
@@ -68,11 +62,49 @@ static void putc(int ch)
#define flush() do { } while (0)
-/*
- * nothing to do
- */
-#define arch_decomp_setup()
+#define MX1_UART1_BASE_ADDR 0x00206000
+#define MX25_UART1_BASE_ADDR 0x43f90000
+#define MX2X_UART1_BASE_ADDR 0x1000a000
+#define MX3X_UART1_BASE_ADDR 0x43F90000
+#define MX3X_UART2_BASE_ADDR 0x43F94000
+
+static __inline__ void __arch_decomp_setup(unsigned long arch_id)
+{
+ switch (arch_id) {
+ case MACH_TYPE_MX1ADS:
+ case MACH_TYPE_SCB9328:
+ uart_base = MX1_UART1_BASE_ADDR;
+ break;
+ case MACH_TYPE_MX25_3DS:
+ uart_base = MX25_UART1_BASE_ADDR;
+ break;
+ case MACH_TYPE_IMX27LITE:
+ case MACH_TYPE_MX27_3DS:
+ case MACH_TYPE_MX27ADS:
+ case MACH_TYPE_PCM038:
+ case MACH_TYPE_MX21ADS:
+ uart_base = MX2X_UART1_BASE_ADDR;
+ break;
+ case MACH_TYPE_MX31LITE:
+ case MACH_TYPE_ARMADILLO5X0:
+ case MACH_TYPE_MX31MOBOARD:
+ case MACH_TYPE_QONG:
+ case MACH_TYPE_MX31_3DS:
+ case MACH_TYPE_PCM037:
+ case MACH_TYPE_MX31ADS:
+ case MACH_TYPE_MX35_3DS:
+ case MACH_TYPE_PCM043:
+ uart_base = MX3X_UART1_BASE_ADDR;
+ break;
+ case MACH_TYPE_MAGX_ZN5:
+ uart_base = MX3X_UART2_BASE_ADDR;
+ break;
+ default:
+ break;
+ }
+}
+#define arch_decomp_setup() __arch_decomp_setup(arch_id)
#define arch_decomp_wdog()
#endif /* __ASM_ARCH_MXC_UNCOMPRESS_H__ */
diff --git a/arch/arm/plat-mxc/iomux-v3.c b/arch/arm/plat-mxc/iomux-v3.c
index 77a078f9513..851ca99bf1b 100644
--- a/arch/arm/plat-mxc/iomux-v3.c
+++ b/arch/arm/plat-mxc/iomux-v3.c
@@ -29,7 +29,7 @@
#include <asm/mach/map.h>
#include <mach/iomux-v3.h>
-#define IOMUX_BASE IO_ADDRESS(IOMUXC_BASE_ADDR)
+static void __iomem *base;
static unsigned long iomux_v3_pad_alloc_map[0x200 / BITS_PER_LONG];
@@ -45,14 +45,14 @@ int mxc_iomux_v3_setup_pad(struct pad_desc *pad)
if (test_and_set_bit(pad_ofs >> 2, iomux_v3_pad_alloc_map))
return -EBUSY;
if (pad->mux_ctrl_ofs)
- __raw_writel(pad->mux_mode, IOMUX_BASE + pad->mux_ctrl_ofs);
+ __raw_writel(pad->mux_mode, base + pad->mux_ctrl_ofs);
if (pad->select_input_ofs)
__raw_writel(pad->select_input,
- IOMUX_BASE + pad->select_input_ofs);
+ base + pad->select_input_ofs);
- if (!(pad->pad_ctrl & NO_PAD_CTRL))
- __raw_writel(pad->pad_ctrl, IOMUX_BASE + pad->pad_ctrl_ofs);
+ if (!(pad->pad_ctrl & NO_PAD_CTRL) && pad->pad_ctrl_ofs)
+ __raw_writel(pad->pad_ctrl, base + pad->pad_ctrl_ofs);
return 0;
}
EXPORT_SYMBOL(mxc_iomux_v3_setup_pad);
@@ -96,3 +96,8 @@ void mxc_iomux_v3_release_multiple_pads(struct pad_desc *pad_list, int count)
}
}
EXPORT_SYMBOL(mxc_iomux_v3_release_multiple_pads);
+
+void mxc_iomux_v3_init(void __iomem *iomux_v3_base)
+{
+ base = iomux_v3_base;
+}
diff --git a/arch/arm/plat-mxc/irq.c b/arch/arm/plat-mxc/irq.c
index 8aee76304f8..778ddfe57d8 100644
--- a/arch/arm/plat-mxc/irq.c
+++ b/arch/arm/plat-mxc/irq.c
@@ -44,7 +44,7 @@
#define AVIC_FIPNDH 0x60 /* fast int pending high */
#define AVIC_FIPNDL 0x64 /* fast int pending low */
-static void __iomem *avic_base;
+void __iomem *avic_base;
int imx_irq_set_priority(unsigned char irq, unsigned char prio)
{
@@ -113,11 +113,11 @@ static struct irq_chip mxc_avic_chip = {
* interrupts. It registers the interrupt enable and disable functions
* to the kernel for each interrupt source.
*/
-void __init mxc_init_irq(void)
+void __init mxc_init_irq(void __iomem *irqbase)
{
int i;
- avic_base = IO_ADDRESS(AVIC_BASE_ADDR);
+ avic_base = irqbase;
/* put the AVIC into the reset value with
* all interrupts disabled
diff --git a/arch/arm/plat-mxc/pwm.c b/arch/arm/plat-mxc/pwm.c
index ae34198a79d..5cdbd605ac0 100644
--- a/arch/arm/plat-mxc/pwm.c
+++ b/arch/arm/plat-mxc/pwm.c
@@ -32,6 +32,7 @@
#define MX3_PWMPR 0x10 /* PWM Period Register */
#define MX3_PWMCR_PRESCALER(x) (((x - 1) & 0xFFF) << 4)
#define MX3_PWMCR_CLKSRC_IPG_HIGH (2 << 16)
+#define MX3_PWMCR_CLKSRC_IPG (1 << 16)
#define MX3_PWMCR_EN (1 << 0)
@@ -55,9 +56,11 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
return -EINVAL;
- if (cpu_is_mx27() || cpu_is_mx3()) {
+ if (cpu_is_mx27() || cpu_is_mx3() || cpu_is_mx25()) {
unsigned long long c;
unsigned long period_cycles, duty_cycles, prescale;
+ u32 cr;
+
c = clk_get_rate(pwm->clk);
c = c * period_ns;
do_div(c, 1000000000);
@@ -72,9 +75,15 @@ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
writel(duty_cycles, pwm->mmio_base + MX3_PWMSAR);
writel(period_cycles, pwm->mmio_base + MX3_PWMPR);
- writel(MX3_PWMCR_PRESCALER(prescale - 1) |
- MX3_PWMCR_CLKSRC_IPG_HIGH | MX3_PWMCR_EN,
- pwm->mmio_base + MX3_PWMCR);
+
+ cr = MX3_PWMCR_PRESCALER(prescale) | MX3_PWMCR_EN;
+
+ if (cpu_is_mx25())
+ cr |= MX3_PWMCR_CLKSRC_IPG;
+ else
+ cr |= MX3_PWMCR_CLKSRC_IPG_HIGH;
+
+ writel(cr, pwm->mmio_base + MX3_PWMCR);
} else if (cpu_is_mx1() || cpu_is_mx21()) {
/* The PWM subsystem allows for exact frequencies. However,
* I cannot connect a scope on my device to the PWM line and
@@ -118,6 +127,8 @@ EXPORT_SYMBOL(pwm_enable);
void pwm_disable(struct pwm_device *pwm)
{
+ writel(0, pwm->mmio_base + MX3_PWMCR);
+
if (pwm->clk_enabled) {
clk_disable(pwm->clk);
pwm->clk_enabled = 0;
diff --git a/arch/arm/plat-mxc/system.c b/arch/arm/plat-mxc/system.c
index 79c37577c91..97f42799fa5 100644
--- a/arch/arm/plat-mxc/system.c
+++ b/arch/arm/plat-mxc/system.c
@@ -27,32 +27,38 @@
#include <linux/delay.h>
#include <mach/hardware.h>
+#include <mach/common.h>
#include <asm/proc-fns.h>
#include <asm/system.h>
-#ifdef CONFIG_ARCH_MX1
-#define WDOG_WCR_REG IO_ADDRESS(WDT_BASE_ADDR)
-#define WDOG_WCR_ENABLE (1 << 0)
-#else
-#define WDOG_WCR_REG IO_ADDRESS(WDOG_BASE_ADDR)
-#define WDOG_WCR_ENABLE (1 << 2)
-#endif
+static void __iomem *wdog_base;
/*
* Reset the system. It is called by machine_restart().
*/
void arch_reset(char mode, const char *cmd)
{
- if (!cpu_is_mx1()) {
+ unsigned int wcr_enable;
+
+#ifdef CONFIG_ARCH_MXC91231
+ if (cpu_is_mxc91231()) {
+ mxc91231_arch_reset(mode, cmd);
+ return;
+ }
+#endif
+ if (cpu_is_mx1()) {
+ wcr_enable = (1 << 0);
+ } else {
struct clk *clk;
clk = clk_get_sys("imx-wdt.0", NULL);
if (!IS_ERR(clk))
clk_enable(clk);
+ wcr_enable = (1 << 2);
}
/* Assert SRS signal */
- __raw_writew(WDOG_WCR_ENABLE, WDOG_WCR_REG);
+ __raw_writew(wcr_enable, wdog_base);
/* wait for reset to assert... */
mdelay(500);
@@ -65,3 +71,8 @@ void arch_reset(char mode, const char *cmd)
/* we'll take a jump through zero as a poor second */
cpu_reset(0);
}
+
+void mxc_arch_reset_init(void __iomem *base)
+{
+ wdog_base = base;
+}
diff --git a/arch/arm/plat-mxc/time.c b/arch/arm/plat-mxc/time.c
index 88fb3a57e02..844567ee35f 100644
--- a/arch/arm/plat-mxc/time.c
+++ b/arch/arm/plat-mxc/time.c
@@ -47,7 +47,7 @@
#define MX2_TSTAT_CAPT (1 << 1)
#define MX2_TSTAT_COMP (1 << 0)
-/* MX31, MX35 */
+/* MX31, MX35, MX25, MXC91231 */
#define MX3_TCTL_WAITEN (1 << 3)
#define MX3_TCTL_CLK_IPG (1 << 6)
#define MX3_TCTL_FRR (1 << 9)
@@ -66,7 +66,7 @@ static inline void gpt_irq_disable(void)
{
unsigned int tmp;
- if (cpu_is_mx3())
+ if (cpu_is_mx3() || cpu_is_mx25())
__raw_writel(0, timer_base + MX3_IR);
else {
tmp = __raw_readl(timer_base + MXC_TCTL);
@@ -76,7 +76,7 @@ static inline void gpt_irq_disable(void)
static inline void gpt_irq_enable(void)
{
- if (cpu_is_mx3())
+ if (cpu_is_mx3() || cpu_is_mx25())
__raw_writel(1<<0, timer_base + MX3_IR);
else {
__raw_writel(__raw_readl(timer_base + MXC_TCTL) | MX1_2_TCTL_IRQEN,
@@ -90,7 +90,7 @@ static void gpt_irq_acknowledge(void)
__raw_writel(0, timer_base + MX1_2_TSTAT);
if (cpu_is_mx2())
__raw_writel(MX2_TSTAT_CAPT | MX2_TSTAT_COMP, timer_base + MX1_2_TSTAT);
- if (cpu_is_mx3())
+ if (cpu_is_mx3() || cpu_is_mx25())
__raw_writel(MX3_TSTAT_OF1, timer_base + MX3_TSTAT);
}
@@ -117,7 +117,7 @@ static int __init mxc_clocksource_init(struct clk *timer_clk)
{
unsigned int c = clk_get_rate(timer_clk);
- if (cpu_is_mx3())
+ if (cpu_is_mx3() || cpu_is_mx25())
clocksource_mxc.read = mx3_get_cycles;
clocksource_mxc.mult = clocksource_hz2mult(c,
@@ -180,7 +180,7 @@ static void mxc_set_mode(enum clock_event_mode mode,
if (mode != clockevent_mode) {
/* Set event time into far-far future */
- if (cpu_is_mx3())
+ if (cpu_is_mx3() || cpu_is_mx25())
__raw_writel(__raw_readl(timer_base + MX3_TCN) - 3,
timer_base + MX3_TCMP);
else
@@ -233,7 +233,7 @@ static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id)
struct clock_event_device *evt = &clockevent_mxc;
uint32_t tstat;
- if (cpu_is_mx3())
+ if (cpu_is_mx3() || cpu_is_mx25())
tstat = __raw_readl(timer_base + MX3_TSTAT);
else
tstat = __raw_readl(timer_base + MX1_2_TSTAT);
@@ -264,7 +264,7 @@ static int __init mxc_clockevent_init(struct clk *timer_clk)
{
unsigned int c = clk_get_rate(timer_clk);
- if (cpu_is_mx3())
+ if (cpu_is_mx3() || cpu_is_mx25())
clockevent_mxc.set_next_event = mx3_set_next_event;
clockevent_mxc.mult = div_sc(c, NSEC_PER_SEC,
@@ -281,30 +281,13 @@ static int __init mxc_clockevent_init(struct clk *timer_clk)
return 0;
}
-void __init mxc_timer_init(struct clk *timer_clk)
+void __init mxc_timer_init(struct clk *timer_clk, void __iomem *base, int irq)
{
uint32_t tctl_val;
- int irq;
clk_enable(timer_clk);
- if (cpu_is_mx1()) {
-#ifdef CONFIG_ARCH_MX1
- timer_base = IO_ADDRESS(TIM1_BASE_ADDR);
- irq = TIM1_INT;
-#endif
- } else if (cpu_is_mx2()) {
-#ifdef CONFIG_ARCH_MX2
- timer_base = IO_ADDRESS(GPT1_BASE_ADDR);
- irq = MXC_INT_GPT1;
-#endif
- } else if (cpu_is_mx3()) {
-#ifdef CONFIG_ARCH_MX3
- timer_base = IO_ADDRESS(GPT1_BASE_ADDR);
- irq = MXC_INT_GPT;
-#endif
- } else
- BUG();
+ timer_base = base;
/*
* Initialise to a known state (all timers off, and timing reset)
@@ -313,7 +296,7 @@ void __init mxc_timer_init(struct clk *timer_clk)
__raw_writel(0, timer_base + MXC_TCTL);
__raw_writel(0, timer_base + MXC_TPRER); /* see datasheet note */
- if (cpu_is_mx3())
+ if (cpu_is_mx3() || cpu_is_mx25())
tctl_val = MX3_TCTL_CLK_IPG | MX3_TCTL_FRR | MX3_TCTL_WAITEN | MXC_TCTL_TEN;
else
tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN;
diff --git a/arch/arm/plat-omap/gpio.c b/arch/arm/plat-omap/gpio.c
index 50b19a3027b..176c86e5531 100644
--- a/arch/arm/plat-omap/gpio.c
+++ b/arch/arm/plat-omap/gpio.c
@@ -138,6 +138,32 @@
#define OMAP24XX_GPIO_CLEARDATAOUT 0x0090
#define OMAP24XX_GPIO_SETDATAOUT 0x0094
+#define OMAP4_GPIO_REVISION 0x0000
+#define OMAP4_GPIO_SYSCONFIG 0x0010
+#define OMAP4_GPIO_EOI 0x0020
+#define OMAP4_GPIO_IRQSTATUSRAW0 0x0024
+#define OMAP4_GPIO_IRQSTATUSRAW1 0x0028
+#define OMAP4_GPIO_IRQSTATUS0 0x002c
+#define OMAP4_GPIO_IRQSTATUS1 0x0030
+#define OMAP4_GPIO_IRQSTATUSSET0 0x0034
+#define OMAP4_GPIO_IRQSTATUSSET1 0x0038
+#define OMAP4_GPIO_IRQSTATUSCLR0 0x003c
+#define OMAP4_GPIO_IRQSTATUSCLR1 0x0040
+#define OMAP4_GPIO_IRQWAKEN0 0x0044
+#define OMAP4_GPIO_IRQWAKEN1 0x0048
+#define OMAP4_GPIO_SYSSTATUS 0x0104
+#define OMAP4_GPIO_CTRL 0x0130
+#define OMAP4_GPIO_OE 0x0134
+#define OMAP4_GPIO_DATAIN 0x0138
+#define OMAP4_GPIO_DATAOUT 0x013c
+#define OMAP4_GPIO_LEVELDETECT0 0x0140
+#define OMAP4_GPIO_LEVELDETECT1 0x0144
+#define OMAP4_GPIO_RISINGDETECT 0x0148
+#define OMAP4_GPIO_FALLINGDETECT 0x014c
+#define OMAP4_GPIO_DEBOUNCENABLE 0x0150
+#define OMAP4_GPIO_DEBOUNCINGTIME 0x0154
+#define OMAP4_GPIO_CLEARDATAOUT 0x0190
+#define OMAP4_GPIO_SETDATAOUT 0x0194
/*
* omap34xx specific GPIO registers
*/
@@ -386,12 +412,16 @@ static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
reg += OMAP850_GPIO_DIR_CONTROL;
break;
#endif
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
case METHOD_GPIO_24XX:
reg += OMAP24XX_GPIO_OE;
break;
#endif
+#if defined(CONFIG_ARCH_OMAP4)
+ case METHOD_GPIO_24XX:
+ reg += OMAP4_GPIO_OE;
+ break;
+#endif
default:
WARN_ON(1);
return;
@@ -459,8 +489,7 @@ static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable)
l &= ~(1 << gpio);
break;
#endif
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
case METHOD_GPIO_24XX:
if (enable)
reg += OMAP24XX_GPIO_SETDATAOUT;
@@ -469,6 +498,15 @@ static void _set_gpio_dataout(struct gpio_bank *bank, int gpio, int enable)
l = 1 << gpio;
break;
#endif
+#ifdef CONFIG_ARCH_OMAP4
+ case METHOD_GPIO_24XX:
+ if (enable)
+ reg += OMAP4_GPIO_SETDATAOUT;
+ else
+ reg += OMAP4_GPIO_CLEARDATAOUT;
+ l = 1 << gpio;
+ break;
+#endif
default:
WARN_ON(1);
return;
@@ -509,12 +547,16 @@ static int _get_gpio_datain(struct gpio_bank *bank, int gpio)
reg += OMAP850_GPIO_DATA_INPUT;
break;
#endif
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
case METHOD_GPIO_24XX:
reg += OMAP24XX_GPIO_DATAIN;
break;
#endif
+#ifdef CONFIG_ARCH_OMAP4
+ case METHOD_GPIO_24XX:
+ reg += OMAP4_GPIO_DATAIN;
+ break;
+#endif
default:
return -EINVAL;
}
@@ -589,7 +631,11 @@ void omap_set_gpio_debounce(int gpio, int enable)
bank = get_gpio_bank(gpio);
reg = bank->base;
+#ifdef CONFIG_ARCH_OMAP4
+ reg += OMAP4_GPIO_DEBOUNCENABLE;
+#else
reg += OMAP24XX_GPIO_DEBOUNCE_EN;
+#endif
spin_lock_irqsave(&bank->lock, flags);
val = __raw_readl(reg);
@@ -626,7 +672,11 @@ void omap_set_gpio_debounce_time(int gpio, int enc_time)
reg = bank->base;
enc_time &= 0xff;
+#ifdef CONFIG_ARCH_OMAP4
+ reg += OMAP4_GPIO_DEBOUNCINGTIME;
+#else
reg += OMAP24XX_GPIO_DEBOUNCE_VAL;
+#endif
__raw_writel(enc_time, reg);
}
EXPORT_SYMBOL(omap_set_gpio_debounce_time);
@@ -638,23 +688,46 @@ static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio,
{
void __iomem *base = bank->base;
u32 gpio_bit = 1 << gpio;
+ u32 val;
- MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT0, gpio_bit,
- trigger & IRQ_TYPE_LEVEL_LOW);
- MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT1, gpio_bit,
- trigger & IRQ_TYPE_LEVEL_HIGH);
- MOD_REG_BIT(OMAP24XX_GPIO_RISINGDETECT, gpio_bit,
- trigger & IRQ_TYPE_EDGE_RISING);
- MOD_REG_BIT(OMAP24XX_GPIO_FALLINGDETECT, gpio_bit,
- trigger & IRQ_TYPE_EDGE_FALLING);
-
+ if (cpu_is_omap44xx()) {
+ MOD_REG_BIT(OMAP4_GPIO_LEVELDETECT0, gpio_bit,
+ trigger & IRQ_TYPE_LEVEL_LOW);
+ MOD_REG_BIT(OMAP4_GPIO_LEVELDETECT1, gpio_bit,
+ trigger & IRQ_TYPE_LEVEL_HIGH);
+ MOD_REG_BIT(OMAP4_GPIO_RISINGDETECT, gpio_bit,
+ trigger & IRQ_TYPE_EDGE_RISING);
+ MOD_REG_BIT(OMAP4_GPIO_FALLINGDETECT, gpio_bit,
+ trigger & IRQ_TYPE_EDGE_FALLING);
+ } else {
+ MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT0, gpio_bit,
+ trigger & IRQ_TYPE_LEVEL_LOW);
+ MOD_REG_BIT(OMAP24XX_GPIO_LEVELDETECT1, gpio_bit,
+ trigger & IRQ_TYPE_LEVEL_HIGH);
+ MOD_REG_BIT(OMAP24XX_GPIO_RISINGDETECT, gpio_bit,
+ trigger & IRQ_TYPE_EDGE_RISING);
+ MOD_REG_BIT(OMAP24XX_GPIO_FALLINGDETECT, gpio_bit,
+ trigger & IRQ_TYPE_EDGE_FALLING);
+ }
if (likely(!(bank->non_wakeup_gpios & gpio_bit))) {
- if (trigger != 0)
- __raw_writel(1 << gpio, bank->base
+ if (cpu_is_omap44xx()) {
+ if (trigger != 0)
+ __raw_writel(1 << gpio, bank->base+
+ OMAP4_GPIO_IRQWAKEN0);
+ else {
+ val = __raw_readl(bank->base +
+ OMAP4_GPIO_IRQWAKEN0);
+ __raw_writel(val & (~(1 << gpio)), bank->base +
+ OMAP4_GPIO_IRQWAKEN0);
+ }
+ } else {
+ if (trigger != 0)
+ __raw_writel(1 << gpio, bank->base
+ OMAP24XX_GPIO_SETWKUENA);
- else
- __raw_writel(1 << gpio, bank->base
+ else
+ __raw_writel(1 << gpio, bank->base
+ OMAP24XX_GPIO_CLEARWKUENA);
+ }
} else {
if (trigger != 0)
bank->enabled_non_wakeup_gpios |= gpio_bit;
@@ -662,9 +735,15 @@ static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio,
bank->enabled_non_wakeup_gpios &= ~gpio_bit;
}
- bank->level_mask =
- __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0) |
- __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1);
+ if (cpu_is_omap44xx()) {
+ bank->level_mask =
+ __raw_readl(bank->base + OMAP4_GPIO_LEVELDETECT0) |
+ __raw_readl(bank->base + OMAP4_GPIO_LEVELDETECT1);
+ } else {
+ bank->level_mask =
+ __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0) |
+ __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1);
+ }
}
#endif
@@ -828,12 +907,16 @@ static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
reg += OMAP850_GPIO_INT_STATUS;
break;
#endif
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
case METHOD_GPIO_24XX:
reg += OMAP24XX_GPIO_IRQSTATUS1;
break;
#endif
+#if defined(CONFIG_ARCH_OMAP4)
+ case METHOD_GPIO_24XX:
+ reg += OMAP4_GPIO_IRQSTATUS0;
+ break;
+#endif
default:
WARN_ON(1);
return;
@@ -843,12 +926,16 @@ static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
/* Workaround for clearing DSP GPIO interrupts to allow retention */
#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
reg = bank->base + OMAP24XX_GPIO_IRQSTATUS2;
- if (cpu_is_omap24xx() || cpu_is_omap34xx())
+#endif
+#if defined(CONFIG_ARCH_OMAP4)
+ reg = bank->base + OMAP4_GPIO_IRQSTATUS1;
+#endif
+ if (cpu_is_omap24xx() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
__raw_writel(gpio_mask, reg);
/* Flush posted write for the irq status to avoid spurious interrupts */
__raw_readl(reg);
-#endif
+ }
}
static inline void _clear_gpio_irqstatus(struct gpio_bank *bank, int gpio)
@@ -898,13 +985,18 @@ static u32 _get_gpio_irqbank_mask(struct gpio_bank *bank)
inv = 1;
break;
#endif
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
case METHOD_GPIO_24XX:
reg += OMAP24XX_GPIO_IRQENABLE1;
mask = 0xffffffff;
break;
#endif
+#if defined(CONFIG_ARCH_OMAP4)
+ case METHOD_GPIO_24XX:
+ reg += OMAP4_GPIO_IRQSTATUSSET0;
+ mask = 0xffffffff;
+ break;
+#endif
default:
WARN_ON(1);
return 0;
@@ -972,8 +1064,7 @@ static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enab
l |= gpio_mask;
break;
#endif
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
case METHOD_GPIO_24XX:
if (enable)
reg += OMAP24XX_GPIO_SETIRQENABLE1;
@@ -982,6 +1073,15 @@ static void _enable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask, int enab
l = gpio_mask;
break;
#endif
+#ifdef CONFIG_ARCH_OMAP4
+ case METHOD_GPIO_24XX:
+ if (enable)
+ reg += OMAP4_GPIO_IRQSTATUSSET0;
+ else
+ reg += OMAP4_GPIO_IRQSTATUSCLR0;
+ l = gpio_mask;
+ break;
+#endif
default:
WARN_ON(1);
return;
@@ -1157,11 +1257,14 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
if (bank->method == METHOD_GPIO_850)
isr_reg = bank->base + OMAP850_GPIO_INT_STATUS;
#endif
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
if (bank->method == METHOD_GPIO_24XX)
isr_reg = bank->base + OMAP24XX_GPIO_IRQSTATUS1;
#endif
+#if defined(CONFIG_ARCH_OMAP4)
+ if (bank->method == METHOD_GPIO_24XX)
+ isr_reg = bank->base + OMAP4_GPIO_IRQSTATUS0;
+#endif
while(1) {
u32 isr_saved, level_mask = 0;
u32 enabled;
@@ -1644,7 +1747,7 @@ static int __init _omap_gpio_init(void)
gpio_bank_count = OMAP34XX_NR_GPIOS;
gpio_bank = gpio_bank_44xx;
- rev = __raw_readl(gpio_bank[0].base + OMAP24XX_GPIO_REVISION);
+ rev = __raw_readl(gpio_bank[0].base + OMAP4_GPIO_REVISION);
printk(KERN_INFO "OMAP44xx GPIO hardware version %d.%d\n",
(rev >> 4) & 0x0f, rev & 0x0f);
}
@@ -1678,7 +1781,16 @@ static int __init _omap_gpio_init(void)
static const u32 non_wakeup_gpios[] = {
0xe203ffc0, 0x08700040
};
-
+ if (cpu_is_omap44xx()) {
+ __raw_writel(0xffffffff, bank->base +
+ OMAP4_GPIO_IRQSTATUSCLR0);
+ __raw_writew(0x0015, bank->base +
+ OMAP4_GPIO_SYSCONFIG);
+ __raw_writel(0x00000000, bank->base +
+ OMAP4_GPIO_DEBOUNCENABLE);
+ /* Initialize interface clock ungated, module enabled */
+ __raw_writel(0, bank->base + OMAP4_GPIO_CTRL);
+ } else {
__raw_writel(0x00000000, bank->base + OMAP24XX_GPIO_IRQENABLE1);
__raw_writel(0xffffffff, bank->base + OMAP24XX_GPIO_IRQSTATUS1);
__raw_writew(0x0015, bank->base + OMAP24XX_GPIO_SYSCONFIG);
@@ -1686,12 +1798,12 @@ static int __init _omap_gpio_init(void)
/* Initialize interface clock ungated, module enabled */
__raw_writel(0, bank->base + OMAP24XX_GPIO_CTRL);
+ }
if (i < ARRAY_SIZE(non_wakeup_gpios))
bank->non_wakeup_gpios = non_wakeup_gpios[i];
gpio_count = 32;
}
#endif
-
/* REVISIT eventually switch from OMAP-specific gpio structs
* over to the generic ones
*/
@@ -1777,14 +1889,20 @@ static int omap_gpio_suspend(struct sys_device *dev, pm_message_t mesg)
wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA;
break;
#endif
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
case METHOD_GPIO_24XX:
wake_status = bank->base + OMAP24XX_GPIO_WAKE_EN;
wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA;
wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA;
break;
#endif
+#ifdef CONFIG_ARCH_OMAP4
+ case METHOD_GPIO_24XX:
+ wake_status = bank->base + OMAP4_GPIO_IRQWAKEN0;
+ wake_clear = bank->base + OMAP4_GPIO_IRQWAKEN0;
+ wake_set = bank->base + OMAP4_GPIO_IRQWAKEN0;
+ break;
+#endif
default:
continue;
}
@@ -1819,13 +1937,18 @@ static int omap_gpio_resume(struct sys_device *dev)
wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA;
break;
#endif
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
case METHOD_GPIO_24XX:
wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA;
wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA;
break;
#endif
+#ifdef CONFIG_ARCH_OMAP4
+ case METHOD_GPIO_24XX:
+ wake_clear = bank->base + OMAP4_GPIO_IRQWAKEN0;
+ wake_set = bank->base + OMAP4_GPIO_IRQWAKEN0;
+ break;
+#endif
default:
continue;
}
@@ -1869,21 +1992,29 @@ void omap2_gpio_prepare_for_retention(void)
if (!(bank->enabled_non_wakeup_gpios))
continue;
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
bank->saved_datain = __raw_readl(bank->base + OMAP24XX_GPIO_DATAIN);
l1 = __raw_readl(bank->base + OMAP24XX_GPIO_FALLINGDETECT);
l2 = __raw_readl(bank->base + OMAP24XX_GPIO_RISINGDETECT);
#endif
+#ifdef CONFIG_ARCH_OMAP4
+ bank->saved_datain = __raw_readl(bank->base +
+ OMAP4_GPIO_DATAIN);
+ l1 = __raw_readl(bank->base + OMAP4_GPIO_FALLINGDETECT);
+ l2 = __raw_readl(bank->base + OMAP4_GPIO_RISINGDETECT);
+#endif
bank->saved_fallingdetect = l1;
bank->saved_risingdetect = l2;
l1 &= ~bank->enabled_non_wakeup_gpios;
l2 &= ~bank->enabled_non_wakeup_gpios;
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
__raw_writel(l1, bank->base + OMAP24XX_GPIO_FALLINGDETECT);
__raw_writel(l2, bank->base + OMAP24XX_GPIO_RISINGDETECT);
#endif
+#ifdef CONFIG_ARCH_OMAP4
+ __raw_writel(l1, bank->base + OMAP4_GPIO_FALLINGDETECT);
+ __raw_writel(l2, bank->base + OMAP4_GPIO_RISINGDETECT);
+#endif
c++;
}
if (!c) {
@@ -1905,27 +2036,29 @@ void omap2_gpio_resume_after_retention(void)
if (!(bank->enabled_non_wakeup_gpios))
continue;
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
__raw_writel(bank->saved_fallingdetect,
bank->base + OMAP24XX_GPIO_FALLINGDETECT);
__raw_writel(bank->saved_risingdetect,
bank->base + OMAP24XX_GPIO_RISINGDETECT);
+ l = __raw_readl(bank->base + OMAP24XX_GPIO_DATAIN);
+#endif
+#ifdef CONFIG_ARCH_OMAP4
+ __raw_writel(bank->saved_fallingdetect,
+ bank->base + OMAP4_GPIO_FALLINGDETECT);
+ __raw_writel(bank->saved_risingdetect,
+ bank->base + OMAP4_GPIO_RISINGDETECT);
+ l = __raw_readl(bank->base + OMAP4_GPIO_DATAIN);
#endif
/* Check if any of the non-wakeup interrupt GPIOs have changed
* state. If so, generate an IRQ by software. This is
* horribly racy, but it's the best we can do to work around
* this silicon bug. */
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
- l = __raw_readl(bank->base + OMAP24XX_GPIO_DATAIN);
-#endif
l ^= bank->saved_datain;
l &= bank->non_wakeup_gpios;
if (l) {
u32 old0, old1;
-#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
- defined(CONFIG_ARCH_OMAP4)
+#if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
old0 = __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0);
old1 = __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1);
__raw_writel(old0 | l, bank->base + OMAP24XX_GPIO_LEVELDETECT0);
@@ -1933,6 +2066,20 @@ void omap2_gpio_resume_after_retention(void)
__raw_writel(old0, bank->base + OMAP24XX_GPIO_LEVELDETECT0);
__raw_writel(old1, bank->base + OMAP24XX_GPIO_LEVELDETECT1);
#endif
+#ifdef CONFIG_ARCH_OMAP4
+ old0 = __raw_readl(bank->base +
+ OMAP4_GPIO_LEVELDETECT0);
+ old1 = __raw_readl(bank->base +
+ OMAP4_GPIO_LEVELDETECT1);
+ __raw_writel(old0 | l, bank->base +
+ OMAP4_GPIO_LEVELDETECT0);
+ __raw_writel(old1 | l, bank->base +
+ OMAP4_GPIO_LEVELDETECT1);
+ __raw_writel(old0, bank->base +
+ OMAP4_GPIO_LEVELDETECT0);
+ __raw_writel(old1, bank->base +
+ OMAP4_GPIO_LEVELDETECT1);
+#endif
}
}
diff --git a/arch/arm/plat-omap/include/mach/dma.h b/arch/arm/plat-omap/include/mach/dma.h
index 7b939cc0196..72f680b7180 100644
--- a/arch/arm/plat-omap/include/mach/dma.h
+++ b/arch/arm/plat-omap/include/mach/dma.h
@@ -122,6 +122,11 @@
#define OMAP_DMA4_CCFN(n) (0x60 * (n) + 0xc0)
#define OMAP_DMA4_COLOR(n) (0x60 * (n) + 0xc4)
+/* Additional registers available on OMAP4 */
+#define OMAP_DMA4_CDP(n) (0x60 * (n) + 0xd0)
+#define OMAP_DMA4_CNDP(n) (0x60 * (n) + 0xd4)
+#define OMAP_DMA4_CCDN(n) (0x60 * (n) + 0xd8)
+
/* Dummy defines to keep multi-omap compiles happy */
#define OMAP1_DMA_REVISION 0
#define OMAP1_DMA_IRQSTATUS_L0 0
@@ -311,6 +316,89 @@
#define OMAP34XX_DMA_USIM_TX 79 /* S_DMA_78 */
#define OMAP34XX_DMA_USIM_RX 80 /* S_DMA_79 */
+/* DMA request lines for 44xx */
+#define OMAP44XX_DMA_DSS_DISPC_REQ 6 /* S_DMA_5 */
+#define OMAP44XX_DMA_SYS_REQ2 7 /* S_DMA_6 */
+#define OMAP44XX_DMA_ISS_REQ1 9 /* S_DMA_8 */
+#define OMAP44XX_DMA_ISS_REQ2 10 /* S_DMA_9 */
+#define OMAP44XX_DMA_ISS_REQ3 12 /* S_DMA_11 */
+#define OMAP44XX_DMA_ISS_REQ4 13 /* S_DMA_12 */
+#define OMAP44XX_DMA_DSS_RFBI_REQ 14 /* S_DMA_13 */
+#define OMAP44XX_DMA_SPI3_TX0 15 /* S_DMA_14 */
+#define OMAP44XX_DMA_SPI3_RX0 16 /* S_DMA_15 */
+#define OMAP44XX_DMA_MCBSP2_TX 17 /* S_DMA_16 */
+#define OMAP44XX_DMA_MCBSP2_RX 18 /* S_DMA_17 */
+#define OMAP44XX_DMA_MCBSP3_TX 19 /* S_DMA_18 */
+#define OMAP44XX_DMA_MCBSP3_RX 20 /* S_DMA_19 */
+#define OMAP44XX_DMA_SPI3_TX1 23 /* S_DMA_22 */
+#define OMAP44XX_DMA_SPI3_RX1 24 /* S_DMA_23 */
+#define OMAP44XX_DMA_I2C3_TX 25 /* S_DMA_24 */
+#define OMAP44XX_DMA_I2C3_RX 26 /* S_DMA_25 */
+#define OMAP44XX_DMA_I2C1_TX 27 /* S_DMA_26 */
+#define OMAP44XX_DMA_I2C1_RX 28 /* S_DMA_27 */
+#define OMAP44XX_DMA_I2C2_TX 29 /* S_DMA_28 */
+#define OMAP44XX_DMA_I2C2_RX 30 /* S_DMA_29 */
+#define OMAP44XX_DMA_MCBSP4_TX 31 /* S_DMA_30 */
+#define OMAP44XX_DMA_MCBSP4_RX 32 /* S_DMA_31 */
+#define OMAP44XX_DMA_MCBSP1_TX 33 /* S_DMA_32 */
+#define OMAP44XX_DMA_MCBSP1_RX 34 /* S_DMA_33 */
+#define OMAP44XX_DMA_SPI1_TX0 35 /* S_DMA_34 */
+#define OMAP44XX_DMA_SPI1_RX0 36 /* S_DMA_35 */
+#define OMAP44XX_DMA_SPI1_TX1 37 /* S_DMA_36 */
+#define OMAP44XX_DMA_SPI1_RX1 38 /* S_DMA_37 */
+#define OMAP44XX_DMA_SPI1_TX2 39 /* S_DMA_38 */
+#define OMAP44XX_DMA_SPI1_RX2 40 /* S_DMA_39 */
+#define OMAP44XX_DMA_SPI1_TX3 41 /* S_DMA_40 */
+#define OMAP44XX_DMA_SPI1_RX3 42 /* S_DMA_41 */
+#define OMAP44XX_DMA_SPI2_TX0 43 /* S_DMA_42 */
+#define OMAP44XX_DMA_SPI2_RX0 44 /* S_DMA_43 */
+#define OMAP44XX_DMA_SPI2_TX1 45 /* S_DMA_44 */
+#define OMAP44XX_DMA_SPI2_RX1 46 /* S_DMA_45 */
+#define OMAP44XX_DMA_MMC2_TX 47 /* S_DMA_46 */
+#define OMAP44XX_DMA_MMC2_RX 48 /* S_DMA_47 */
+#define OMAP44XX_DMA_UART1_TX 49 /* S_DMA_48 */
+#define OMAP44XX_DMA_UART1_RX 50 /* S_DMA_49 */
+#define OMAP44XX_DMA_UART2_TX 51 /* S_DMA_50 */
+#define OMAP44XX_DMA_UART2_RX 52 /* S_DMA_51 */
+#define OMAP44XX_DMA_UART3_TX 53 /* S_DMA_52 */
+#define OMAP44XX_DMA_UART3_RX 54 /* S_DMA_53 */
+#define OMAP44XX_DMA_UART4_TX 55 /* S_DMA_54 */
+#define OMAP44XX_DMA_UART4_RX 56 /* S_DMA_55 */
+#define OMAP44XX_DMA_MMC4_TX 57 /* S_DMA_56 */
+#define OMAP44XX_DMA_MMC4_RX 58 /* S_DMA_57 */
+#define OMAP44XX_DMA_MMC5_TX 59 /* S_DMA_58 */
+#define OMAP44XX_DMA_MMC5_RX 60 /* S_DMA_59 */
+#define OMAP44XX_DMA_MMC1_TX 61 /* S_DMA_60 */
+#define OMAP44XX_DMA_MMC1_RX 62 /* S_DMA_61 */
+#define OMAP44XX_DMA_SYS_REQ3 64 /* S_DMA_63 */
+#define OMAP44XX_DMA_MCPDM_UP 65 /* S_DMA_64 */
+#define OMAP44XX_DMA_MCPDM_DL 66 /* S_DMA_65 */
+#define OMAP44XX_DMA_SPI4_TX0 70 /* S_DMA_69 */
+#define OMAP44XX_DMA_SPI4_RX0 71 /* S_DMA_70 */
+#define OMAP44XX_DMA_DSS_DSI1_REQ0 72 /* S_DMA_71 */
+#define OMAP44XX_DMA_DSS_DSI1_REQ1 73 /* S_DMA_72 */
+#define OMAP44XX_DMA_DSS_DSI1_REQ2 74 /* S_DMA_73 */
+#define OMAP44XX_DMA_DSS_DSI1_REQ3 75 /* S_DMA_74 */
+#define OMAP44XX_DMA_DSS_HDMI_REQ 76 /* S_DMA_75 */
+#define OMAP44XX_DMA_MMC3_TX 77 /* S_DMA_76 */
+#define OMAP44XX_DMA_MMC3_RX 78 /* S_DMA_77 */
+#define OMAP44XX_DMA_USIM_TX 79 /* S_DMA_78 */
+#define OMAP44XX_DMA_USIM_RX 80 /* S_DMA_79 */
+#define OMAP44XX_DMA_DSS_DSI2_REQ0 81 /* S_DMA_80 */
+#define OMAP44XX_DMA_DSS_DSI2_REQ1 82 /* S_DMA_81 */
+#define OMAP44XX_DMA_DSS_DSI2_REQ2 83 /* S_DMA_82 */
+#define OMAP44XX_DMA_DSS_DSI2_REQ3 84 /* S_DMA_83 */
+#define OMAP44XX_DMA_ABE_REQ0 101 /* S_DMA_100 */
+#define OMAP44XX_DMA_ABE_REQ1 102 /* S_DMA_101 */
+#define OMAP44XX_DMA_ABE_REQ2 103 /* S_DMA_102 */
+#define OMAP44XX_DMA_ABE_REQ3 104 /* S_DMA_103 */
+#define OMAP44XX_DMA_ABE_REQ4 105 /* S_DMA_104 */
+#define OMAP44XX_DMA_ABE_REQ5 106 /* S_DMA_105 */
+#define OMAP44XX_DMA_ABE_REQ6 107 /* S_DMA_106 */
+#define OMAP44XX_DMA_ABE_REQ7 108 /* S_DMA_107 */
+#define OMAP44XX_DMA_I2C4_TX 124 /* S_DMA_123 */
+#define OMAP44XX_DMA_I2C4_RX 125 /* S_DMA_124 */
+
/*----------------------------------------------------------------------------*/
/* Hardware registers for LCD DMA */
diff --git a/arch/arm/plat-omap/include/mach/mcbsp.h b/arch/arm/plat-omap/include/mach/mcbsp.h
index 63a3f254af7..e0d6eca222c 100644
--- a/arch/arm/plat-omap/include/mach/mcbsp.h
+++ b/arch/arm/plat-omap/include/mach/mcbsp.h
@@ -53,6 +53,11 @@
#define OMAP34XX_MCBSP4_BASE 0x49026000
#define OMAP34XX_MCBSP5_BASE 0x48096000
+#define OMAP44XX_MCBSP1_BASE 0x49022000
+#define OMAP44XX_MCBSP2_BASE 0x49024000
+#define OMAP44XX_MCBSP3_BASE 0x49026000
+#define OMAP44XX_MCBSP4_BASE 0x48074000
+
#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP730)
#define OMAP_MCBSP_REG_DRR2 0x00
@@ -98,7 +103,8 @@
#define AUDIO_DMA_TX OMAP_DMA_MCBSP1_TX
#define AUDIO_DMA_RX OMAP_DMA_MCBSP1_RX
-#elif defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
+#elif defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX) || \
+ defined(CONFIG_ARCH_OMAP4)
#define OMAP_MCBSP_REG_DRR2 0x00
#define OMAP_MCBSP_REG_DRR1 0x04
diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c
index 8dc7927906f..88ac9768f1c 100644
--- a/arch/arm/plat-omap/mcbsp.c
+++ b/arch/arm/plat-omap/mcbsp.c
@@ -191,7 +191,7 @@ void omap_mcbsp_config(unsigned int id, const struct omap_mcbsp_reg_cfg *config)
OMAP_MCBSP_WRITE(io_base, MCR2, config->mcr2);
OMAP_MCBSP_WRITE(io_base, MCR1, config->mcr1);
OMAP_MCBSP_WRITE(io_base, PCR0, config->pcr0);
- if (cpu_is_omap2430() || cpu_is_omap34xx()) {
+ if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
OMAP_MCBSP_WRITE(io_base, XCCR, config->xccr);
OMAP_MCBSP_WRITE(io_base, RCCR, config->rccr);
}
diff --git a/arch/arm/plat-s3c/Kconfig b/arch/arm/plat-s3c/Kconfig
index 935c7558469..8931c5f0e46 100644
--- a/arch/arm/plat-s3c/Kconfig
+++ b/arch/arm/plat-s3c/Kconfig
@@ -198,4 +198,9 @@ config S3C_DEV_USB_HSOTG
help
Compile in platform device definition for USB high-speed OtG
+config S3C_DEV_NAND
+ bool
+ help
+ Compile in platform device definition for NAND controller
+
endif
diff --git a/arch/arm/plat-s3c/Makefile b/arch/arm/plat-s3c/Makefile
index 0761766b183..3c09109e9e8 100644
--- a/arch/arm/plat-s3c/Makefile
+++ b/arch/arm/plat-s3c/Makefile
@@ -28,13 +28,17 @@ obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_PM) += pm-gpio.o
obj-$(CONFIG_S3C2410_PM_CHECK) += pm-check.o
+# PWM support
+
+obj-$(CONFIG_HAVE_PWM) += pwm.o
+
# devices
obj-$(CONFIG_S3C_DEV_HSMMC) += dev-hsmmc.o
obj-$(CONFIG_S3C_DEV_HSMMC1) += dev-hsmmc1.o
obj-y += dev-i2c0.o
obj-$(CONFIG_S3C_DEV_I2C1) += dev-i2c1.o
-obj-$(CONFIG_SND_S3C64XX_SOC_I2S) += dev-audio.o
obj-$(CONFIG_S3C_DEV_FB) += dev-fb.o
obj-$(CONFIG_S3C_DEV_USB_HOST) += dev-usb.o
obj-$(CONFIG_S3C_DEV_USB_HSOTG) += dev-usb-hsotg.o
+obj-$(CONFIG_S3C_DEV_NAND) += dev-nand.o
diff --git a/arch/arm/plat-s3c/dev-nand.c b/arch/arm/plat-s3c/dev-nand.c
new file mode 100644
index 00000000000..4e532373243
--- /dev/null
+++ b/arch/arm/plat-s3c/dev-nand.c
@@ -0,0 +1,30 @@
+/*
+ * S3C series device definition for nand device
+ *
+ * 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/kernel.h>
+#include <linux/platform_device.h>
+
+#include <mach/map.h>
+#include <plat/devs.h>
+
+static struct resource s3c_nand_resource[] = {
+ [0] = {
+ .start = S3C_PA_NAND,
+ .end = S3C_PA_NAND + SZ_1M,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+struct platform_device s3c_device_nand = {
+ .name = "s3c2410-nand",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(s3c_nand_resource),
+ .resource = s3c_nand_resource,
+};
+
+EXPORT_SYMBOL(s3c_device_nand);
diff --git a/arch/arm/plat-s3c/include/plat/adc.h b/arch/arm/plat-s3c/include/plat/adc.h
index d847bd476b6..5f3b1cd53b9 100644
--- a/arch/arm/plat-s3c/include/plat/adc.h
+++ b/arch/arm/plat-s3c/include/plat/adc.h
@@ -19,10 +19,14 @@ struct s3c_adc_client;
extern int s3c_adc_start(struct s3c_adc_client *client,
unsigned int channel, unsigned int nr_samples);
+extern int s3c_adc_read(struct s3c_adc_client *client, unsigned int ch);
+
extern struct s3c_adc_client *
s3c_adc_register(struct platform_device *pdev,
- void (*select)(unsigned selected),
- void (*conv)(unsigned d0, unsigned d1,
+ void (*select)(struct s3c_adc_client *client,
+ unsigned selected),
+ void (*conv)(struct s3c_adc_client *client,
+ unsigned d0, unsigned d1,
unsigned *samples_left),
unsigned int is_ts);
diff --git a/arch/arm/plat-s3c/include/plat/cpu-freq.h b/arch/arm/plat-s3c/include/plat/cpu-freq.h
index c86a13307e9..7b982b7f28c 100644
--- a/arch/arm/plat-s3c/include/plat/cpu-freq.h
+++ b/arch/arm/plat-s3c/include/plat/cpu-freq.h
@@ -17,6 +17,21 @@ struct s3c_cpufreq_info;
struct s3c_cpufreq_board;
struct s3c_iotimings;
+/**
+ * struct s3c_freq - frequency information (mainly for core drivers)
+ * @fclk: The FCLK frequency in Hz.
+ * @armclk: The ARMCLK frequency in Hz.
+ * @hclk_tns: HCLK cycle time in 10ths of nano-seconds.
+ * @hclk: The HCLK frequency in Hz.
+ * @pclk: The PCLK frequency in Hz.
+ *
+ * This contains the frequency information about the current configuration
+ * mainly for the core drivers to ensure we do not end up passing about
+ * a large number of parameters.
+ *
+ * The @hclk_tns field is a useful cache for the parts of the drivers that
+ * need to calculate IO timings and suchlike.
+ */
struct s3c_freq {
unsigned long fclk;
unsigned long armclk;
@@ -25,48 +40,84 @@ struct s3c_freq {
unsigned long pclk;
};
-/* wrapper 'struct cpufreq_freqs' so that any drivers receiving the
+/**
+ * struct s3c_cpufreq_freqs - s3c cpufreq notification information.
+ * @freqs: The cpufreq setting information.
+ * @old: The old clock settings.
+ * @new: The new clock settings.
+ * @pll_changing: Set if the PLL is changing.
+ *
+ * Wrapper 'struct cpufreq_freqs' so that any drivers receiving the
* notification can use this information that is not provided by just
* having the core frequency alone.
+ *
+ * The pll_changing flag is used to indicate if the PLL itself is
+ * being set during this change. This is important as the clocks
+ * will temporarily be set to the XTAL clock during this time, so
+ * drivers may want to close down their output during this time.
+ *
+ * Note, this is not being used by any current drivers and therefore
+ * may be removed in the future.
*/
-
struct s3c_cpufreq_freqs {
struct cpufreq_freqs freqs;
struct s3c_freq old;
struct s3c_freq new;
+
+ unsigned int pll_changing:1;
};
#define to_s3c_cpufreq(_cf) container_of(_cf, struct s3c_cpufreq_freqs, freqs)
+/**
+ * struct s3c_clkdivs - clock divisor information
+ * @p_divisor: Divisor from FCLK to PCLK.
+ * @h_divisor: Divisor from FCLK to HCLK.
+ * @arm_divisor: Divisor from FCLK to ARMCLK (not all CPUs).
+ * @dvs: Non-zero if using DVS mode for ARMCLK.
+ *
+ * Divisor settings for the core clocks.
+ */
struct s3c_clkdivs {
- int p_divisor; /* fclk / pclk */
- int h_divisor; /* fclk / hclk */
- int arm_divisor; /* not all cpus have this. */
- unsigned char dvs; /* using dvs mode to arm. */
+ int p_divisor;
+ int h_divisor;
+ int arm_divisor;
+ unsigned char dvs;
};
#define PLLVAL(_m, _p, _s) (((_m) << 12) | ((_p) << 4) | (_s))
+/**
+ * struct s3c_pllval - PLL value entry.
+ * @freq: The frequency for this entry in Hz.
+ * @pll_reg: The PLL register setting for this PLL value.
+ */
struct s3c_pllval {
unsigned long freq;
unsigned long pll_reg;
};
-struct s3c_cpufreq_config {
- struct s3c_freq freq;
- struct s3c_pllval pll;
- struct s3c_clkdivs divs;
- struct s3c_cpufreq_info *info; /* for core, not drivers */
- struct s3c_cpufreq_board *board;
-};
-
-/* s3c_cpufreq_board
+/**
+ * struct s3c_cpufreq_board - per-board cpu frequency informatin
+ * @refresh: The SDRAM refresh period in nanoseconds.
+ * @auto_io: Set if the IO timing settings should be generated from the
+ * initialisation time hardware registers.
+ * @need_io: Set if the board has external IO on any of the chipselect
+ * lines that will require the hardware timing registers to be
+ * updated on a clock change.
+ * @max: The maxium frequency limits for the system. Any field that
+ * is left at zero will use the CPU's settings.
+ *
+ * This contains the board specific settings that affect how the CPU
+ * drivers chose settings. These include the memory refresh and IO
+ * timing information.
*
- * per-board configuraton information, such as memory refresh and
- * how to initialise IO timings.
+ * Registration depends on the driver being used, the ARMCLK only
+ * implementation does not currently need this but the older style
+ * driver requires this to be available.
*/
struct s3c_cpufreq_board {
- unsigned int refresh; /* refresh period in ns */
+ unsigned int refresh;
unsigned int auto_io:1; /* automatically init io timings. */
unsigned int need_io:1; /* set if needs io timing support. */
diff --git a/arch/arm/plat-s3c/include/plat/cpu.h b/arch/arm/plat-s3c/include/plat/cpu.h
index be541cbba07..fbc3d498e02 100644
--- a/arch/arm/plat-s3c/include/plat/cpu.h
+++ b/arch/arm/plat-s3c/include/plat/cpu.h
@@ -65,6 +65,7 @@ extern struct sys_timer s3c24xx_timer;
/* system device classes */
extern struct sysdev_class s3c2410_sysclass;
+extern struct sysdev_class s3c2410a_sysclass;
extern struct sysdev_class s3c2412_sysclass;
extern struct sysdev_class s3c2440_sysclass;
extern struct sysdev_class s3c2442_sysclass;
diff --git a/arch/arm/plat-s3c/include/plat/devs.h b/arch/arm/plat-s3c/include/plat/devs.h
index 2e170827e0b..0f540ea1e99 100644
--- a/arch/arm/plat-s3c/include/plat/devs.h
+++ b/arch/arm/plat-s3c/include/plat/devs.h
@@ -46,6 +46,8 @@ extern struct platform_device s3c_device_hsmmc2;
extern struct platform_device s3c_device_spi0;
extern struct platform_device s3c_device_spi1;
+extern struct platform_device s3c_device_hwmon;
+
extern struct platform_device s3c_device_nand;
extern struct platform_device s3c_device_usbgadget;
@@ -56,5 +58,6 @@ extern struct platform_device s3c_device_usb_hsotg;
#ifdef CONFIG_CPU_S3C2440
extern struct platform_device s3c_device_camif;
+extern struct platform_device s3c_device_ac97;
#endif
diff --git a/arch/arm/plat-s3c/include/plat/hwmon.h b/arch/arm/plat-s3c/include/plat/hwmon.h
new file mode 100644
index 00000000000..1ba88ea0aa3
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/hwmon.h
@@ -0,0 +1,41 @@
+/* linux/arch/arm/plat-s3c/include/plat/hwmon.h
+ *
+ * Copyright 2005 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ * http://armlinux.simtec.co.uk/
+ *
+ * S3C - HWMon interface for ADC
+ *
+ * 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_ARCH_ADC_HWMON_H
+#define __ASM_ARCH_ADC_HWMON_H __FILE__
+
+/**
+ * s3c_hwmon_chcfg - channel configuration
+ * @name: The name to give this channel.
+ * @mult: Multiply the ADC value read by this.
+ * @div: Divide the value from the ADC by this.
+ *
+ * The value read from the ADC is converted to a value that
+ * hwmon expects (mV) by result = (value_read * @mult) / @div.
+ */
+struct s3c_hwmon_chcfg {
+ const char *name;
+ unsigned int mult;
+ unsigned int div;
+};
+
+/**
+ * s3c_hwmon_pdata - HWMON platform data
+ * @in: One configuration for each possible channel used.
+ */
+struct s3c_hwmon_pdata {
+ struct s3c_hwmon_chcfg *in[8];
+};
+
+#endif /* __ASM_ARCH_ADC_HWMON_H */
+
diff --git a/arch/arm/plat-s3c/include/plat/map-base.h b/arch/arm/plat-s3c/include/plat/map-base.h
index b84289d32a5..250be311c85 100644
--- a/arch/arm/plat-s3c/include/plat/map-base.h
+++ b/arch/arm/plat-s3c/include/plat/map-base.h
@@ -32,9 +32,15 @@
#define S3C_VA_IRQ S3C_ADDR(0x00000000) /* irq controller(s) */
#define S3C_VA_SYS S3C_ADDR(0x00100000) /* system control */
-#define S3C_VA_MEM S3C_ADDR(0x00200000) /* system control */
+#define S3C_VA_MEM S3C_ADDR(0x00200000) /* memory control */
#define S3C_VA_TIMER S3C_ADDR(0x00300000) /* timer block */
#define S3C_VA_WATCHDOG S3C_ADDR(0x00400000) /* watchdog */
#define S3C_VA_UART S3C_ADDR(0x01000000) /* UART */
+/* This is used for the CPU specific mappings that may be needed, so that
+ * they do not need to directly used S3C_ADDR() and thus make it easier to
+ * modify the space for mapping.
+ */
+#define S3C_ADDR_CPU(x) S3C_ADDR(0x00500000 + (x))
+
#endif /* __ASM_PLAT_MAP_H */
diff --git a/arch/arm/plat-s3c24xx/pwm.c b/arch/arm/plat-s3c/pwm.c
index 82a6d4de02a..4fdc5b307fd 100644
--- a/arch/arm/plat-s3c24xx/pwm.c
+++ b/arch/arm/plat-s3c/pwm.c
@@ -1,10 +1,10 @@
-/* arch/arm/plat-s3c24xx/pwm.c
+/* arch/arm/plat-s3c/pwm.c
*
* Copyright (c) 2007 Ben Dooks
* Copyright (c) 2008 Simtec Electronics
* Ben Dooks <ben@simtec.co.uk>, <ben-linux@fluff.org>
*
- * S3C24XX PWM device core
+ * S3C series PWM device core
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,6 +20,7 @@
#include <linux/pwm.h>
#include <mach/irqs.h>
+#include <mach/map.h>
#include <plat/devs.h>
#include <plat/regs-timer.h>
diff --git a/arch/arm/plat-s3c24xx/Kconfig b/arch/arm/plat-s3c24xx/Kconfig
index 5b0bc914f58..9c7aca48964 100644
--- a/arch/arm/plat-s3c24xx/Kconfig
+++ b/arch/arm/plat-s3c24xx/Kconfig
@@ -10,6 +10,7 @@ config PLAT_S3C24XX
default y
select NO_IOPORT
select ARCH_REQUIRE_GPIOLIB
+ select S3C_DEVICE_NAND
help
Base platform code for any Samsung S3C24XX device
@@ -34,6 +35,40 @@ config CPU_S3C244X
help
Support for S3C2440 and S3C2442 Samsung Mobile CPU based systems.
+config S3C2440_CPUFREQ
+ bool "S3C2440/S3C2442 CPU Frequency scaling support"
+ depends on CPU_FREQ_S3C24XX && (CPU_S3C2440 || CPU_S3C2442)
+ select S3C2410_CPUFREQ_UTILS
+ default y
+ help
+ CPU Frequency scaling support for S3C2440 and S3C2442 SoC CPUs.
+
+config S3C2440_XTAL_12000000
+ bool
+ help
+ Indicate that the build needs to support 12MHz system
+ crystal.
+
+config S3C2440_XTAL_16934400
+ bool
+ help
+ Indicate that the build needs to support 16.9344MHz system
+ crystal.
+
+config S3C2440_PLL_12000000
+ bool
+ depends on S3C2440_CPUFREQ && S3C2440_XTAL_12000000
+ default y if CPU_FREQ_S3C24XX_PLL
+ help
+ PLL tables for S3C2440 or S3C2442 CPUs with 12MHz crystals.
+
+config S3C2440_PLL_16934400
+ bool
+ depends on S3C2440_CPUFREQ && S3C2440_XTAL_16934400
+ default y if CPU_FREQ_S3C24XX_PLL
+ help
+ PLL tables for S3C2440 or S3C2442 CPUs with 16.934MHz crystals.
+
config S3C24XX_PWM
bool "PWM device support"
select HAVE_PWM
@@ -105,8 +140,39 @@ config S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7
SPI GPIO configuration code for BUS 1 when connected to
GPG5, GPG6 and GPG7.
+config S3C24XX_SPI_BUS1_GPD8_GPD9_GPD10
+ bool
+ help
+ SPI GPIO configuration code for BUS 1 when connected to
+ GPD8, GPD9 and GPD10.
+
# common code for s3c24xx based machines, such as the SMDKs.
+# cpu frequency items common between s3c2410 and s3c2440/s3c2442
+
+config S3C2410_IOTIMING
+ bool
+ depends on CPU_FREQ_S3C24XX
+ help
+ Internal node to select io timing code that is common to the s3c2410
+ and s3c2440/s3c2442 cpu frequency support.
+
+config S3C2410_CPUFREQ_UTILS
+ bool
+ depends on CPU_FREQ_S3C24XX
+ help
+ Internal node to select timing code that is common to the s3c2410
+ and s3c2440/s3c244 cpu frequency support.
+
+# cpu frequency support common to s3c2412, s3c2413 and s3c2442
+
+config S3C2412_IOTIMING
+ bool
+ depends on CPU_FREQ_S3C24XX && (CPU_S3C2412 || CPU_S3C2443)
+ help
+ Intel node to select io timing code that is common to the s3c2412
+ and the s3c2443.
+
config MACH_SMDK
bool
help
diff --git a/arch/arm/plat-s3c24xx/Makefile b/arch/arm/plat-s3c24xx/Makefile
index 579a165c282..7780d2dd833 100644
--- a/arch/arm/plat-s3c24xx/Makefile
+++ b/arch/arm/plat-s3c24xx/Makefile
@@ -20,19 +20,28 @@ obj-y += gpiolib.o
obj-y += clock.o
obj-$(CONFIG_S3C24XX_DCLK) += clock-dclk.o
+obj-$(CONFIG_CPU_FREQ_S3C24XX) += cpu-freq.o
+obj-$(CONFIG_CPU_FREQ_S3C24XX_DEBUGFS) += cpu-freq-debugfs.o
+
# Architecture dependant builds
obj-$(CONFIG_CPU_S3C244X) += s3c244x.o
obj-$(CONFIG_CPU_S3C244X) += s3c244x-irq.o
obj-$(CONFIG_CPU_S3C244X) += s3c244x-clock.o
+obj-$(CONFIG_S3C2440_CPUFREQ) += s3c2440-cpufreq.o
+obj-$(CONFIG_S3C2440_PLL_12000000) += s3c2440-pll-12000000.o
+obj-$(CONFIG_S3C2440_PLL_16934400) += s3c2440-pll-16934400.o
+
obj-$(CONFIG_PM_SIMTEC) += pm-simtec.o
obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_PM) += irq-pm.o
obj-$(CONFIG_PM) += sleep.o
-obj-$(CONFIG_S3C24XX_PWM) += pwm.o
obj-$(CONFIG_S3C2410_CLOCK) += s3c2410-clock.o
obj-$(CONFIG_S3C2410_DMA) += dma.o
obj-$(CONFIG_S3C24XX_ADC) += adc.o
+obj-$(CONFIG_S3C2410_IOTIMING) += s3c2410-iotiming.o
+obj-$(CONFIG_S3C2412_IOTIMING) += s3c2412-iotiming.o
+obj-$(CONFIG_S3C2410_CPUFREQ_UTILS) += s3c2410-cpufreq-utils.o
# device specific setup and/or initialisation
obj-$(CONFIG_ARCH_S3C2410) += setup-i2c.o
@@ -41,6 +50,7 @@ obj-$(CONFIG_ARCH_S3C2410) += setup-i2c.o
obj-$(CONFIG_S3C24XX_SPI_BUS0_GPE11_GPE12_GPE13) += spi-bus0-gpe11_12_13.o
obj-$(CONFIG_S3C24XX_SPI_BUS1_GPG5_GPG6_GPG7) += spi-bus1-gpg5_6_7.o
+obj-$(CONFIG_S3C24XX_SPI_BUS1_GPD8_GPD9_GPD10) += spi-bus1-gpd8_9_10.o
# machine common support
diff --git a/arch/arm/plat-s3c24xx/adc.c b/arch/arm/plat-s3c24xx/adc.c
index ee1baf11ad9..11117a7ba91 100644
--- a/arch/arm/plat-s3c24xx/adc.c
+++ b/arch/arm/plat-s3c24xx/adc.c
@@ -39,13 +39,16 @@
struct s3c_adc_client {
struct platform_device *pdev;
struct list_head pend;
+ wait_queue_head_t *wait;
unsigned int nr_samples;
+ int result;
unsigned char is_ts;
unsigned char channel;
- void (*select_cb)(unsigned selected);
- void (*convert_cb)(unsigned val1, unsigned val2,
+ void (*select_cb)(struct s3c_adc_client *c, unsigned selected);
+ void (*convert_cb)(struct s3c_adc_client *c,
+ unsigned val1, unsigned val2,
unsigned *samples_left);
};
@@ -81,7 +84,7 @@ static inline void s3c_adc_select(struct adc_device *adc,
{
unsigned con = readl(adc->regs + S3C2410_ADCCON);
- client->select_cb(1);
+ client->select_cb(client, 1);
con &= ~S3C2410_ADCCON_MUXMASK;
con &= ~S3C2410_ADCCON_STDBM;
@@ -153,25 +156,61 @@ int s3c_adc_start(struct s3c_adc_client *client,
}
EXPORT_SYMBOL_GPL(s3c_adc_start);
-static void s3c_adc_default_select(unsigned select)
+static void s3c_convert_done(struct s3c_adc_client *client,
+ unsigned v, unsigned u, unsigned *left)
+{
+ client->result = v;
+ wake_up(client->wait);
+}
+
+int s3c_adc_read(struct s3c_adc_client *client, unsigned int ch)
+{
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
+ int ret;
+
+ client->convert_cb = s3c_convert_done;
+ client->wait = &wake;
+ client->result = -1;
+
+ ret = s3c_adc_start(client, ch, 1);
+ if (ret < 0)
+ goto err;
+
+ ret = wait_event_timeout(wake, client->result >= 0, HZ / 2);
+ if (client->result < 0) {
+ ret = -ETIMEDOUT;
+ goto err;
+ }
+
+ client->convert_cb = NULL;
+ return client->result;
+
+err:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(s3c_adc_convert);
+
+static void s3c_adc_default_select(struct s3c_adc_client *client,
+ unsigned select)
{
}
struct s3c_adc_client *s3c_adc_register(struct platform_device *pdev,
- void (*select)(unsigned int selected),
- void (*conv)(unsigned d0, unsigned d1,
+ void (*select)(struct s3c_adc_client *client,
+ unsigned int selected),
+ void (*conv)(struct s3c_adc_client *client,
+ unsigned d0, unsigned d1,
unsigned *samples_left),
unsigned int is_ts)
{
struct s3c_adc_client *client;
WARN_ON(!pdev);
- WARN_ON(!conv);
if (!select)
select = s3c_adc_default_select;
- if (!conv || !pdev)
+ if (!pdev)
return ERR_PTR(-EINVAL);
client = kzalloc(sizeof(struct s3c_adc_client), GFP_KERNEL);
@@ -230,16 +269,19 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw)
adc_dbg(adc, "read %d: 0x%04x, 0x%04x\n", client->nr_samples, data0, data1);
client->nr_samples--;
- (client->convert_cb)(data0 & 0x3ff, data1 & 0x3ff, &client->nr_samples);
+
+ if (client->convert_cb)
+ (client->convert_cb)(client, data0 & 0x3ff, data1 & 0x3ff,
+ &client->nr_samples);
if (client->nr_samples > 0) {
/* fire another conversion for this */
- client->select_cb(1);
+ client->select_cb(client, 1);
s3c_adc_convert(adc);
} else {
local_irq_save(flags);
- (client->select_cb)(0);
+ (client->select_cb)(client, 0);
adc->cur = NULL;
s3c_adc_try(adc);
diff --git a/arch/arm/plat-s3c24xx/cpu-freq-debugfs.c b/arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
new file mode 100644
index 00000000000..a9276667c2f
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
@@ -0,0 +1,199 @@
+/* linux/arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
+ *
+ * Copyright (c) 2009 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX CPU Frequency scaling - debugfs status support
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/cpufreq.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/err.h>
+
+#include <plat/cpu-freq-core.h>
+
+static struct dentry *dbgfs_root;
+static struct dentry *dbgfs_file_io;
+static struct dentry *dbgfs_file_info;
+static struct dentry *dbgfs_file_board;
+
+#define print_ns(x) ((x) / 10), ((x) % 10)
+
+static void show_max(struct seq_file *seq, struct s3c_freq *f)
+{
+ seq_printf(seq, "MAX: F=%lu, H=%lu, P=%lu, A=%lu\n",
+ f->fclk, f->hclk, f->pclk, f->armclk);
+}
+
+static int board_show(struct seq_file *seq, void *p)
+{
+ struct s3c_cpufreq_config *cfg;
+ struct s3c_cpufreq_board *brd;
+
+ cfg = s3c_cpufreq_getconfig();
+ if (!cfg) {
+ seq_printf(seq, "no configuration registered\n");
+ return 0;
+ }
+
+ brd = cfg->board;
+ if (!brd) {
+ seq_printf(seq, "no board definition set?\n");
+ return 0;
+ }
+
+ seq_printf(seq, "SDRAM refresh %u ns\n", brd->refresh);
+ seq_printf(seq, "auto_io=%u\n", brd->auto_io);
+ seq_printf(seq, "need_io=%u\n", brd->need_io);
+
+ show_max(seq, &brd->max);
+
+
+ return 0;
+}
+
+static int fops_board_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, board_show, NULL);
+}
+
+static const struct file_operations fops_board = {
+ .open = fops_board_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int info_show(struct seq_file *seq, void *p)
+{
+ struct s3c_cpufreq_config *cfg;
+
+ cfg = s3c_cpufreq_getconfig();
+ if (!cfg) {
+ seq_printf(seq, "no configuration registered\n");
+ return 0;
+ }
+
+ seq_printf(seq, " FCLK %ld Hz\n", cfg->freq.fclk);
+ seq_printf(seq, " HCLK %ld Hz (%lu.%lu ns)\n",
+ cfg->freq.hclk, print_ns(cfg->freq.hclk_tns));
+ seq_printf(seq, " PCLK %ld Hz\n", cfg->freq.hclk);
+ seq_printf(seq, "ARMCLK %ld Hz\n", cfg->freq.armclk);
+ seq_printf(seq, "\n");
+
+ show_max(seq, &cfg->max);
+
+ seq_printf(seq, "Divisors: P=%d, H=%d, A=%d, dvs=%s\n",
+ cfg->divs.h_divisor, cfg->divs.p_divisor,
+ cfg->divs.arm_divisor, cfg->divs.dvs ? "on" : "off");
+ seq_printf(seq, "\n");
+
+ seq_printf(seq, "lock_pll=%u\n", cfg->lock_pll);
+
+ return 0;
+}
+
+static int fops_info_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, info_show, NULL);
+}
+
+static const struct file_operations fops_info = {
+ .open = fops_info_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int io_show(struct seq_file *seq, void *p)
+{
+ void (*show_bank)(struct seq_file *, struct s3c_cpufreq_config *, union s3c_iobank *);
+ struct s3c_cpufreq_config *cfg;
+ struct s3c_iotimings *iot;
+ union s3c_iobank *iob;
+ int bank;
+
+ cfg = s3c_cpufreq_getconfig();
+ if (!cfg) {
+ seq_printf(seq, "no configuration registered\n");
+ return 0;
+ }
+
+ show_bank = cfg->info->debug_io_show;
+ if (!show_bank) {
+ seq_printf(seq, "no code to show bank timing\n");
+ return 0;
+ }
+
+ iot = s3c_cpufreq_getiotimings();
+ if (!iot) {
+ seq_printf(seq, "no io timings registered\n");
+ return 0;
+ }
+
+ seq_printf(seq, "hclk period is %lu.%lu ns\n", print_ns(cfg->freq.hclk_tns));
+
+ for (bank = 0; bank < MAX_BANKS; bank++) {
+ iob = &iot->bank[bank];
+
+ seq_printf(seq, "bank %d: ", bank);
+
+ if (!iob->io_2410) {
+ seq_printf(seq, "nothing set\n");
+ continue;
+ }
+
+ show_bank(seq, cfg, iob);
+ }
+
+ return 0;
+}
+
+static int fops_io_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, io_show, NULL);
+}
+
+static const struct file_operations fops_io = {
+ .open = fops_io_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+
+static int __init s3c_freq_debugfs_init(void)
+{
+ dbgfs_root = debugfs_create_dir("s3c-cpufreq", NULL);
+ if (IS_ERR(dbgfs_root)) {
+ printk(KERN_ERR "%s: error creating debugfs root\n", __func__);
+ return PTR_ERR(dbgfs_root);
+ }
+
+ dbgfs_file_io = debugfs_create_file("io-timing", S_IRUGO, dbgfs_root,
+ NULL, &fops_io);
+
+ dbgfs_file_info = debugfs_create_file("info", S_IRUGO, dbgfs_root,
+ NULL, &fops_info);
+
+ dbgfs_file_board = debugfs_create_file("board", S_IRUGO, dbgfs_root,
+ NULL, &fops_board);
+
+ return 0;
+}
+
+late_initcall(s3c_freq_debugfs_init);
+
diff --git a/arch/arm/plat-s3c24xx/cpu-freq.c b/arch/arm/plat-s3c24xx/cpu-freq.c
new file mode 100644
index 00000000000..4f1b789a117
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/cpu-freq.c
@@ -0,0 +1,716 @@
+/* linux/arch/arm/plat-s3c24xx/cpu-freq.c
+ *
+ * Copyright (c) 2006,2007,2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX CPU Frequency scaling
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/cpufreq.h>
+#include <linux/cpu.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/sysdev.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <plat/cpu.h>
+#include <plat/clock.h>
+#include <plat/cpu-freq-core.h>
+
+#include <mach/regs-clock.h>
+
+/* note, cpufreq support deals in kHz, no Hz */
+
+static struct cpufreq_driver s3c24xx_driver;
+static struct s3c_cpufreq_config cpu_cur;
+static struct s3c_iotimings s3c24xx_iotiming;
+static struct cpufreq_frequency_table *pll_reg;
+static unsigned int last_target = ~0;
+static unsigned int ftab_size;
+static struct cpufreq_frequency_table *ftab;
+
+static struct clk *_clk_mpll;
+static struct clk *_clk_xtal;
+static struct clk *clk_fclk;
+static struct clk *clk_hclk;
+static struct clk *clk_pclk;
+static struct clk *clk_arm;
+
+#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUGFS
+struct s3c_cpufreq_config *s3c_cpufreq_getconfig(void)
+{
+ return &cpu_cur;
+}
+
+struct s3c_iotimings *s3c_cpufreq_getiotimings(void)
+{
+ return &s3c24xx_iotiming;
+}
+#endif /* CONFIG_CPU_FREQ_S3C24XX_DEBUGFS */
+
+static void s3c_cpufreq_getcur(struct s3c_cpufreq_config *cfg)
+{
+ unsigned long fclk, pclk, hclk, armclk;
+
+ cfg->freq.fclk = fclk = clk_get_rate(clk_fclk);
+ cfg->freq.hclk = hclk = clk_get_rate(clk_hclk);
+ cfg->freq.pclk = pclk = clk_get_rate(clk_pclk);
+ cfg->freq.armclk = armclk = clk_get_rate(clk_arm);
+
+ cfg->pll.index = __raw_readl(S3C2410_MPLLCON);
+ cfg->pll.frequency = fclk;
+
+ cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10);
+
+ cfg->divs.h_divisor = fclk / hclk;
+ cfg->divs.p_divisor = fclk / pclk;
+}
+
+static inline void s3c_cpufreq_calc(struct s3c_cpufreq_config *cfg)
+{
+ unsigned long pll = cfg->pll.frequency;
+
+ cfg->freq.fclk = pll;
+ cfg->freq.hclk = pll / cfg->divs.h_divisor;
+ cfg->freq.pclk = pll / cfg->divs.p_divisor;
+
+ /* convert hclk into 10ths of nanoseconds for io calcs */
+ cfg->freq.hclk_tns = 1000000000 / (cfg->freq.hclk / 10);
+}
+
+static inline int closer(unsigned int target, unsigned int n, unsigned int c)
+{
+ int diff_cur = abs(target - c);
+ int diff_new = abs(target - n);
+
+ return (diff_new < diff_cur);
+}
+
+static void s3c_cpufreq_show(const char *pfx,
+ struct s3c_cpufreq_config *cfg)
+{
+ s3c_freq_dbg("%s: Fvco=%u, F=%lu, A=%lu, H=%lu (%u), P=%lu (%u)\n",
+ pfx, cfg->pll.frequency, cfg->freq.fclk, cfg->freq.armclk,
+ cfg->freq.hclk, cfg->divs.h_divisor,
+ cfg->freq.pclk, cfg->divs.p_divisor);
+}
+
+/* functions to wrapper the driver info calls to do the cpu specific work */
+
+static void s3c_cpufreq_setio(struct s3c_cpufreq_config *cfg)
+{
+ if (cfg->info->set_iotiming)
+ (cfg->info->set_iotiming)(cfg, &s3c24xx_iotiming);
+}
+
+static int s3c_cpufreq_calcio(struct s3c_cpufreq_config *cfg)
+{
+ if (cfg->info->calc_iotiming)
+ return (cfg->info->calc_iotiming)(cfg, &s3c24xx_iotiming);
+
+ return 0;
+}
+
+static void s3c_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg)
+{
+ (cfg->info->set_refresh)(cfg);
+}
+
+static void s3c_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
+{
+ (cfg->info->set_divs)(cfg);
+}
+
+static int s3c_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
+{
+ return (cfg->info->calc_divs)(cfg);
+}
+
+static void s3c_cpufreq_setfvco(struct s3c_cpufreq_config *cfg)
+{
+ (cfg->info->set_fvco)(cfg);
+}
+
+static inline void s3c_cpufreq_resume_clocks(void)
+{
+ cpu_cur.info->resume_clocks();
+}
+
+static inline void s3c_cpufreq_updateclk(struct clk *clk,
+ unsigned int freq)
+{
+ clk_set_rate(clk, freq);
+}
+
+static int s3c_cpufreq_settarget(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ struct cpufreq_frequency_table *pll)
+{
+ struct s3c_cpufreq_freqs freqs;
+ struct s3c_cpufreq_config cpu_new;
+ unsigned long flags;
+
+ cpu_new = cpu_cur; /* copy new from current */
+
+ s3c_cpufreq_show("cur", &cpu_cur);
+
+ /* TODO - check for DMA currently outstanding */
+
+ cpu_new.pll = pll ? *pll : cpu_cur.pll;
+
+ if (pll)
+ freqs.pll_changing = 1;
+
+ /* update our frequencies */
+
+ cpu_new.freq.armclk = target_freq;
+ cpu_new.freq.fclk = cpu_new.pll.frequency;
+
+ if (s3c_cpufreq_calcdivs(&cpu_new) < 0) {
+ printk(KERN_ERR "no divisors for %d\n", target_freq);
+ goto err_notpossible;
+ }
+
+ s3c_freq_dbg("%s: got divs\n", __func__);
+
+ s3c_cpufreq_calc(&cpu_new);
+
+ s3c_freq_dbg("%s: calculated frequencies for new\n", __func__);
+
+ if (cpu_new.freq.hclk != cpu_cur.freq.hclk) {
+ if (s3c_cpufreq_calcio(&cpu_new) < 0) {
+ printk(KERN_ERR "%s: no IO timings\n", __func__);
+ goto err_notpossible;
+ }
+ }
+
+ s3c_cpufreq_show("new", &cpu_new);
+
+ /* setup our cpufreq parameters */
+
+ freqs.old = cpu_cur.freq;
+ freqs.new = cpu_new.freq;
+
+ freqs.freqs.cpu = 0;
+ freqs.freqs.old = cpu_cur.freq.armclk / 1000;
+ freqs.freqs.new = cpu_new.freq.armclk / 1000;
+
+ /* update f/h/p clock settings before we issue the change
+ * notification, so that drivers do not need to do anything
+ * special if they want to recalculate on CPUFREQ_PRECHANGE. */
+
+ s3c_cpufreq_updateclk(_clk_mpll, cpu_new.pll.frequency);
+ s3c_cpufreq_updateclk(clk_fclk, cpu_new.freq.fclk);
+ s3c_cpufreq_updateclk(clk_hclk, cpu_new.freq.hclk);
+ s3c_cpufreq_updateclk(clk_pclk, cpu_new.freq.pclk);
+
+ /* start the frequency change */
+
+ if (policy)
+ cpufreq_notify_transition(&freqs.freqs, CPUFREQ_PRECHANGE);
+
+ /* If hclk is staying the same, then we do not need to
+ * re-write the IO or the refresh timings whilst we are changing
+ * speed. */
+
+ local_irq_save(flags);
+
+ /* is our memory clock slowing down? */
+ if (cpu_new.freq.hclk < cpu_cur.freq.hclk) {
+ s3c_cpufreq_setrefresh(&cpu_new);
+ s3c_cpufreq_setio(&cpu_new);
+ }
+
+ if (cpu_new.freq.fclk == cpu_cur.freq.fclk) {
+ /* not changing PLL, just set the divisors */
+
+ s3c_cpufreq_setdivs(&cpu_new);
+ } else {
+ if (cpu_new.freq.fclk < cpu_cur.freq.fclk) {
+ /* slow the cpu down, then set divisors */
+
+ s3c_cpufreq_setfvco(&cpu_new);
+ s3c_cpufreq_setdivs(&cpu_new);
+ } else {
+ /* set the divisors, then speed up */
+
+ s3c_cpufreq_setdivs(&cpu_new);
+ s3c_cpufreq_setfvco(&cpu_new);
+ }
+ }
+
+ /* did our memory clock speed up */
+ if (cpu_new.freq.hclk > cpu_cur.freq.hclk) {
+ s3c_cpufreq_setrefresh(&cpu_new);
+ s3c_cpufreq_setio(&cpu_new);
+ }
+
+ /* update our current settings */
+ cpu_cur = cpu_new;
+
+ local_irq_restore(flags);
+
+ /* notify everyone we've done this */
+ if (policy)
+ cpufreq_notify_transition(&freqs.freqs, CPUFREQ_POSTCHANGE);
+
+ s3c_freq_dbg("%s: finished\n", __func__);
+ return 0;
+
+ err_notpossible:
+ printk(KERN_ERR "no compatible settings for %d\n", target_freq);
+ return -EINVAL;
+}
+
+/* s3c_cpufreq_target
+ *
+ * called by the cpufreq core to adjust the frequency that the CPU
+ * is currently running at.
+ */
+
+static int s3c_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ struct cpufreq_frequency_table *pll;
+ unsigned int index;
+
+ /* avoid repeated calls which cause a needless amout of duplicated
+ * logging output (and CPU time as the calculation process is
+ * done) */
+ if (target_freq == last_target)
+ return 0;
+
+ last_target = target_freq;
+
+ s3c_freq_dbg("%s: policy %p, target %u, relation %u\n",
+ __func__, policy, target_freq, relation);
+
+ if (ftab) {
+ if (cpufreq_frequency_table_target(policy, ftab,
+ target_freq, relation,
+ &index)) {
+ s3c_freq_dbg("%s: table failed\n", __func__);
+ return -EINVAL;
+ }
+
+ s3c_freq_dbg("%s: adjust %d to entry %d (%u)\n", __func__,
+ target_freq, index, ftab[index].frequency);
+ target_freq = ftab[index].frequency;
+ }
+
+ target_freq *= 1000; /* convert target to Hz */
+
+ /* find the settings for our new frequency */
+
+ if (!pll_reg || cpu_cur.lock_pll) {
+ /* either we've not got any PLL values, or we've locked
+ * to the current one. */
+ pll = NULL;
+ } else {
+ struct cpufreq_policy tmp_policy;
+ int ret;
+
+ /* we keep the cpu pll table in Hz, to ensure we get an
+ * accurate value for the PLL output. */
+
+ tmp_policy.min = policy->min * 1000;
+ tmp_policy.max = policy->max * 1000;
+ tmp_policy.cpu = policy->cpu;
+
+ /* cpufreq_frequency_table_target uses a pointer to 'index'
+ * which is the number of the table entry, not the value of
+ * the table entry's index field. */
+
+ ret = cpufreq_frequency_table_target(&tmp_policy, pll_reg,
+ target_freq, relation,
+ &index);
+
+ if (ret < 0) {
+ printk(KERN_ERR "%s: no PLL available\n", __func__);
+ goto err_notpossible;
+ }
+
+ pll = pll_reg + index;
+
+ s3c_freq_dbg("%s: target %u => %u\n",
+ __func__, target_freq, pll->frequency);
+
+ target_freq = pll->frequency;
+ }
+
+ return s3c_cpufreq_settarget(policy, target_freq, pll);
+
+ err_notpossible:
+ printk(KERN_ERR "no compatible settings for %d\n", target_freq);
+ return -EINVAL;
+}
+
+static unsigned int s3c_cpufreq_get(unsigned int cpu)
+{
+ return clk_get_rate(clk_arm) / 1000;
+}
+
+struct clk *s3c_cpufreq_clk_get(struct device *dev, const char *name)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, name);
+ if (IS_ERR(clk))
+ printk(KERN_ERR "cpufreq: failed to get clock '%s'\n", name);
+
+ return clk;
+}
+
+static int s3c_cpufreq_init(struct cpufreq_policy *policy)
+{
+ printk(KERN_INFO "%s: initialising policy %p\n", __func__, policy);
+
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ policy->cur = s3c_cpufreq_get(0);
+ policy->min = policy->cpuinfo.min_freq = 0;
+ policy->max = policy->cpuinfo.max_freq = cpu_cur.info->max.fclk / 1000;
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+
+ /* feed the latency information from the cpu driver */
+ policy->cpuinfo.transition_latency = cpu_cur.info->latency;
+
+ if (ftab)
+ cpufreq_frequency_table_cpuinfo(policy, ftab);
+
+ return 0;
+}
+
+static __init int s3c_cpufreq_initclks(void)
+{
+ _clk_mpll = s3c_cpufreq_clk_get(NULL, "mpll");
+ _clk_xtal = s3c_cpufreq_clk_get(NULL, "xtal");
+ clk_fclk = s3c_cpufreq_clk_get(NULL, "fclk");
+ clk_hclk = s3c_cpufreq_clk_get(NULL, "hclk");
+ clk_pclk = s3c_cpufreq_clk_get(NULL, "pclk");
+ clk_arm = s3c_cpufreq_clk_get(NULL, "armclk");
+
+ if (IS_ERR(clk_fclk) || IS_ERR(clk_hclk) || IS_ERR(clk_pclk) ||
+ IS_ERR(_clk_mpll) || IS_ERR(clk_arm) || IS_ERR(_clk_xtal)) {
+ printk(KERN_ERR "%s: could not get clock(s)\n", __func__);
+ return -ENOENT;
+ }
+
+ printk(KERN_INFO "%s: clocks f=%lu,h=%lu,p=%lu,a=%lu\n", __func__,
+ clk_get_rate(clk_fclk) / 1000,
+ clk_get_rate(clk_hclk) / 1000,
+ clk_get_rate(clk_pclk) / 1000,
+ clk_get_rate(clk_arm) / 1000);
+
+ return 0;
+}
+
+static int s3c_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static struct cpufreq_frequency_table suspend_pll;
+static unsigned int suspend_freq;
+
+static int s3c_cpufreq_suspend(struct cpufreq_policy *policy, pm_message_t pmsg)
+{
+ suspend_pll.frequency = clk_get_rate(_clk_mpll);
+ suspend_pll.index = __raw_readl(S3C2410_MPLLCON);
+ suspend_freq = s3c_cpufreq_get(0) * 1000;
+
+ return 0;
+}
+
+static int s3c_cpufreq_resume(struct cpufreq_policy *policy)
+{
+ int ret;
+
+ s3c_freq_dbg("%s: resuming with policy %p\n", __func__, policy);
+
+ last_target = ~0; /* invalidate last_target setting */
+
+ /* first, find out what speed we resumed at. */
+ s3c_cpufreq_resume_clocks();
+
+ /* whilst we will be called later on, we try and re-set the
+ * cpu frequencies as soon as possible so that we do not end
+ * up resuming devices and then immediatley having to re-set
+ * a number of settings once these devices have restarted.
+ *
+ * as a note, it is expected devices are not used until they
+ * have been un-suspended and at that time they should have
+ * used the updated clock settings.
+ */
+
+ ret = s3c_cpufreq_settarget(NULL, suspend_freq, &suspend_pll);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to reset pll/freq\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+#else
+#define s3c_cpufreq_resume NULL
+#define s3c_cpufreq_suspend NULL
+#endif
+
+static struct cpufreq_driver s3c24xx_driver = {
+ .flags = CPUFREQ_STICKY,
+ .verify = s3c_cpufreq_verify,
+ .target = s3c_cpufreq_target,
+ .get = s3c_cpufreq_get,
+ .init = s3c_cpufreq_init,
+ .suspend = s3c_cpufreq_suspend,
+ .resume = s3c_cpufreq_resume,
+ .name = "s3c24xx",
+};
+
+
+int __init s3c_cpufreq_register(struct s3c_cpufreq_info *info)
+{
+ if (!info || !info->name) {
+ printk(KERN_ERR "%s: failed to pass valid information\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "S3C24XX CPU Frequency driver, %s cpu support\n",
+ info->name);
+
+ /* check our driver info has valid data */
+
+ BUG_ON(info->set_refresh == NULL);
+ BUG_ON(info->set_divs == NULL);
+ BUG_ON(info->calc_divs == NULL);
+
+ /* info->set_fvco is optional, depending on whether there
+ * is a need to set the clock code. */
+
+ cpu_cur.info = info;
+
+ /* Note, driver registering should probably update locktime */
+
+ return 0;
+}
+
+int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board)
+{
+ struct s3c_cpufreq_board *ours;
+
+ if (!board) {
+ printk(KERN_INFO "%s: no board data\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Copy the board information so that each board can make this
+ * initdata. */
+
+ ours = kzalloc(sizeof(struct s3c_cpufreq_board), GFP_KERNEL);
+ if (ours == NULL) {
+ printk(KERN_ERR "%s: no memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ *ours = *board;
+ cpu_cur.board = ours;
+
+ return 0;
+}
+
+int __init s3c_cpufreq_auto_io(void)
+{
+ int ret;
+
+ if (!cpu_cur.info->get_iotiming) {
+ printk(KERN_ERR "%s: get_iotiming undefined\n", __func__);
+ return -ENOENT;
+ }
+
+ printk(KERN_INFO "%s: working out IO settings\n", __func__);
+
+ ret = (cpu_cur.info->get_iotiming)(&cpu_cur, &s3c24xx_iotiming);
+ if (ret)
+ printk(KERN_ERR "%s: failed to get timings\n", __func__);
+
+ return ret;
+}
+
+/* if one or is zero, then return the other, otherwise return the min */
+#define do_min(_a, _b) ((_a) == 0 ? (_b) : (_b) == 0 ? (_a) : min(_a, _b))
+
+/**
+ * s3c_cpufreq_freq_min - find the minimum settings for the given freq.
+ * @dst: The destination structure
+ * @a: One argument.
+ * @b: The other argument.
+ *
+ * Create a minimum of each frequency entry in the 'struct s3c_freq',
+ * unless the entry is zero when it is ignored and the non-zero argument
+ * used.
+ */
+static void s3c_cpufreq_freq_min(struct s3c_freq *dst,
+ struct s3c_freq *a, struct s3c_freq *b)
+{
+ dst->fclk = do_min(a->fclk, b->fclk);
+ dst->hclk = do_min(a->hclk, b->hclk);
+ dst->pclk = do_min(a->pclk, b->pclk);
+ dst->armclk = do_min(a->armclk, b->armclk);
+}
+
+static inline u32 calc_locktime(u32 freq, u32 time_us)
+{
+ u32 result;
+
+ result = freq * time_us;
+ result = DIV_ROUND_UP(result, 1000 * 1000);
+
+ return result;
+}
+
+static void s3c_cpufreq_update_loctkime(void)
+{
+ unsigned int bits = cpu_cur.info->locktime_bits;
+ u32 rate = (u32)clk_get_rate(_clk_xtal);
+ u32 val;
+
+ if (bits == 0) {
+ WARN_ON(1);
+ return;
+ }
+
+ val = calc_locktime(rate, cpu_cur.info->locktime_u) << bits;
+ val |= calc_locktime(rate, cpu_cur.info->locktime_m);
+
+ printk(KERN_INFO "%s: new locktime is 0x%08x\n", __func__, val);
+ __raw_writel(val, S3C2410_LOCKTIME);
+}
+
+static int s3c_cpufreq_build_freq(void)
+{
+ int size, ret;
+
+ if (!cpu_cur.info->calc_freqtable)
+ return -EINVAL;
+
+ kfree(ftab);
+ ftab = NULL;
+
+ size = cpu_cur.info->calc_freqtable(&cpu_cur, NULL, 0);
+ size++;
+
+ ftab = kmalloc(sizeof(struct cpufreq_frequency_table) * size, GFP_KERNEL);
+ if (!ftab) {
+ printk(KERN_ERR "%s: no memory for tables\n", __func__);
+ return -ENOMEM;
+ }
+
+ ftab_size = size;
+
+ ret = cpu_cur.info->calc_freqtable(&cpu_cur, ftab, size);
+ s3c_cpufreq_addfreq(ftab, ret, size, CPUFREQ_TABLE_END);
+
+ return 0;
+}
+
+static int __init s3c_cpufreq_initcall(void)
+{
+ int ret = 0;
+
+ if (cpu_cur.info && cpu_cur.board) {
+ ret = s3c_cpufreq_initclks();
+ if (ret)
+ goto out;
+
+ /* get current settings */
+ s3c_cpufreq_getcur(&cpu_cur);
+ s3c_cpufreq_show("cur", &cpu_cur);
+
+ if (cpu_cur.board->auto_io) {
+ ret = s3c_cpufreq_auto_io();
+ if (ret) {
+ printk(KERN_ERR "%s: failed to get io timing\n",
+ __func__);
+ goto out;
+ }
+ }
+
+ if (cpu_cur.board->need_io && !cpu_cur.info->set_iotiming) {
+ printk(KERN_ERR "%s: no IO support registered\n",
+ __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!cpu_cur.info->need_pll)
+ cpu_cur.lock_pll = 1;
+
+ s3c_cpufreq_update_loctkime();
+
+ s3c_cpufreq_freq_min(&cpu_cur.max, &cpu_cur.board->max,
+ &cpu_cur.info->max);
+
+ if (cpu_cur.info->calc_freqtable)
+ s3c_cpufreq_build_freq();
+
+ ret = cpufreq_register_driver(&s3c24xx_driver);
+ }
+
+ out:
+ return ret;
+}
+
+late_initcall(s3c_cpufreq_initcall);
+
+/**
+ * s3c_plltab_register - register CPU PLL table.
+ * @plls: The list of PLL entries.
+ * @plls_no: The size of the PLL entries @plls.
+ *
+ * Register the given set of PLLs with the system.
+ */
+int __init s3c_plltab_register(struct cpufreq_frequency_table *plls,
+ unsigned int plls_no)
+{
+ struct cpufreq_frequency_table *vals;
+ unsigned int size;
+
+ size = sizeof(struct cpufreq_frequency_table) * (plls_no + 1);
+
+ vals = kmalloc(size, GFP_KERNEL);
+ if (vals) {
+ memcpy(vals, plls, size);
+ pll_reg = vals;
+
+ /* write a terminating entry, we don't store it in the
+ * table that is stored in the kernel */
+ vals += plls_no;
+ vals->frequency = CPUFREQ_TABLE_END;
+
+ printk(KERN_INFO "cpufreq: %d PLL entries\n", plls_no);
+ } else
+ printk(KERN_ERR "cpufreq: no memory for PLL tables\n");
+
+ return vals ? 0 : -ENOMEM;
+}
diff --git a/arch/arm/plat-s3c24xx/cpu.c b/arch/arm/plat-s3c24xx/cpu.c
index 1932b7e0da1..5447e60f393 100644
--- a/arch/arm/plat-s3c24xx/cpu.c
+++ b/arch/arm/plat-s3c24xx/cpu.c
@@ -81,7 +81,7 @@ static struct cpu_table cpu_ids[] __initdata = {
.map_io = s3c2410_map_io,
.init_clocks = s3c2410_init_clocks,
.init_uarts = s3c2410_init_uarts,
- .init = s3c2410_init,
+ .init = s3c2410a_init,
.name = name_s3c2410a
},
{
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
index 4eb378c89a3..f52a92ce8dd 100644
--- a/arch/arm/plat-s3c24xx/devs.c
+++ b/arch/arm/plat-s3c24xx/devs.c
@@ -26,6 +26,8 @@
#include <asm/mach/irq.h>
#include <mach/fb.h>
#include <mach/hardware.h>
+#include <mach/dma.h>
+#include <mach/irqs.h>
#include <asm/irq.h>
#include <plat/regs-serial.h>
@@ -180,25 +182,6 @@ void __init s3c24xx_fb_set_platdata(struct s3c2410fb_mach_info *pd)
}
}
-/* NAND Controller */
-
-static struct resource s3c_nand_resource[] = {
- [0] = {
- .start = S3C24XX_PA_NAND,
- .end = S3C24XX_PA_NAND + S3C24XX_SZ_NAND - 1,
- .flags = IORESOURCE_MEM,
- }
-};
-
-struct platform_device s3c_device_nand = {
- .name = "s3c2410-nand",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_nand_resource),
- .resource = s3c_nand_resource,
-};
-
-EXPORT_SYMBOL(s3c_device_nand);
-
/* USB Device (Gadget)*/
static struct resource s3c_usbgadget_resource[] = {
@@ -348,7 +331,7 @@ struct platform_device s3c_device_adc = {
/* HWMON */
struct platform_device s3c_device_hwmon = {
- .name = "s3c24xx-hwmon",
+ .name = "s3c-hwmon",
.id = -1,
.dev.parent = &s3c_device_adc.dev,
};
@@ -473,4 +456,52 @@ struct platform_device s3c_device_camif = {
EXPORT_SYMBOL(s3c_device_camif);
+/* AC97 */
+
+static struct resource s3c_ac97_resource[] = {
+ [0] = {
+ .start = S3C2440_PA_AC97,
+ .end = S3C2440_PA_AC97 + S3C2440_SZ_AC97 -1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3C244x_AC97,
+ .end = IRQ_S3C244x_AC97,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .name = "PCM out",
+ .start = DMACH_PCM_OUT,
+ .end = DMACH_PCM_OUT,
+ .flags = IORESOURCE_DMA,
+ },
+ [3] = {
+ .name = "PCM in",
+ .start = DMACH_PCM_IN,
+ .end = DMACH_PCM_IN,
+ .flags = IORESOURCE_DMA,
+ },
+ [4] = {
+ .name = "Mic in",
+ .start = DMACH_MIC_IN,
+ .end = DMACH_MIC_IN,
+ .flags = IORESOURCE_DMA,
+ },
+};
+
+static u64 s3c_device_ac97_dmamask = 0xffffffffUL;
+
+struct platform_device s3c_device_ac97 = {
+ .name = "s3c-ac97",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(s3c_ac97_resource),
+ .resource = s3c_ac97_resource,
+ .dev = {
+ .dma_mask = &s3c_device_ac97_dmamask,
+ .coherent_dma_mask = 0xffffffffUL
+ }
+};
+
+EXPORT_SYMBOL(s3c_device_ac97);
+
#endif // CONFIG_CPU_S32440
diff --git a/arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h b/arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h
new file mode 100644
index 00000000000..efeb025affc
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/include/plat/cpu-freq-core.h
@@ -0,0 +1,282 @@
+/* arch/arm/plat-s3c/include/plat/cpu-freq.h
+ *
+ * Copyright (c) 2006,2007,2009 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C CPU frequency scaling support - core support
+ *
+ * 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 <plat/cpu-freq.h>
+
+struct seq_file;
+
+#define MAX_BANKS (8)
+#define S3C2412_MAX_IO (8)
+
+/**
+ * struct s3c2410_iobank_timing - IO bank timings for S3C2410 style timings
+ * @bankcon: The cached version of settings in this structure.
+ * @tacp:
+ * @tacs: Time from address valid to nCS asserted.
+ * @tcos: Time from nCS asserted to nOE or nWE asserted.
+ * @tacc: Time that nOE or nWE is asserted.
+ * @tcoh: Time nCS is held after nOE or nWE are released.
+ * @tcah: Time address is held for after
+ * @nwait_en: Whether nWAIT is enabled for this bank.
+ *
+ * This structure represents the IO timings for a S3C2410 style IO bank
+ * used by the CPU frequency support if it needs to change the settings
+ * of the IO.
+ */
+struct s3c2410_iobank_timing {
+ unsigned long bankcon;
+ unsigned int tacp;
+ unsigned int tacs;
+ unsigned int tcos;
+ unsigned int tacc;
+ unsigned int tcoh; /* nCS hold afrer nOE/nWE */
+ unsigned int tcah; /* Address hold after nCS */
+ unsigned char nwait_en; /* nWait enabled for bank. */
+};
+
+/**
+ * struct s3c2412_iobank_timing - io timings for PL092 (S3C2412) style IO
+ * @idcy: The idle cycle time between transactions.
+ * @wstrd: nCS release to end of read cycle.
+ * @wstwr: nCS release to end of write cycle.
+ * @wstoen: nCS assertion to nOE assertion time.
+ * @wstwen: nCS assertion to nWE assertion time.
+ * @wstbrd: Burst ready delay.
+ * @smbidcyr: Register cache for smbidcyr value.
+ * @smbwstrd: Register cache for smbwstrd value.
+ * @smbwstwr: Register cache for smbwstwr value.
+ * @smbwstoen: Register cache for smbwstoen value.
+ * @smbwstwen: Register cache for smbwstwen value.
+ * @smbwstbrd: Register cache for smbwstbrd value.
+ *
+ * Timing information for a IO bank on an S3C2412 or similar system which
+ * uses a PL093 block.
+ */
+struct s3c2412_iobank_timing {
+ unsigned int idcy;
+ unsigned int wstrd;
+ unsigned int wstwr;
+ unsigned int wstoen;
+ unsigned int wstwen;
+ unsigned int wstbrd;
+
+ /* register cache */
+ unsigned char smbidcyr;
+ unsigned char smbwstrd;
+ unsigned char smbwstwr;
+ unsigned char smbwstoen;
+ unsigned char smbwstwen;
+ unsigned char smbwstbrd;
+};
+
+union s3c_iobank {
+ struct s3c2410_iobank_timing *io_2410;
+ struct s3c2412_iobank_timing *io_2412;
+};
+
+/**
+ * struct s3c_iotimings - Chip IO timings holder
+ * @bank: The timings for each IO bank.
+ */
+struct s3c_iotimings {
+ union s3c_iobank bank[MAX_BANKS];
+};
+
+/**
+ * struct s3c_plltab - PLL table information.
+ * @vals: List of PLL values.
+ * @size: Size of the PLL table @vals.
+ */
+struct s3c_plltab {
+ struct s3c_pllval *vals;
+ int size;
+};
+
+/**
+ * struct s3c_cpufreq_config - current cpu frequency configuration
+ * @freq: The current settings for the core clocks.
+ * @max: Maxium settings, derived from core, board and user settings.
+ * @pll: The PLL table entry for the current PLL settings.
+ * @divs: The divisor settings for the core clocks.
+ * @info: The current core driver information.
+ * @board: The information for the board we are running on.
+ * @lock_pll: Set if the PLL settings cannot be changed.
+ *
+ * This is for the core drivers that need to know information about
+ * the current settings and values. It should not be needed by any
+ * device drivers.
+*/
+struct s3c_cpufreq_config {
+ struct s3c_freq freq;
+ struct s3c_freq max;
+ struct cpufreq_frequency_table pll;
+ struct s3c_clkdivs divs;
+ struct s3c_cpufreq_info *info; /* for core, not drivers */
+ struct s3c_cpufreq_board *board;
+
+ unsigned int lock_pll:1;
+};
+
+/**
+ * struct s3c_cpufreq_info - Information for the CPU frequency driver.
+ * @name: The name of this implementation.
+ * @max: The maximum frequencies for the system.
+ * @latency: Transition latency to give to cpufreq.
+ * @locktime_m: The lock-time in uS for the MPLL.
+ * @locktime_u: The lock-time in uS for the UPLL.
+ * @locttime_bits: The number of bits each LOCKTIME field.
+ * @need_pll: Set if this driver needs to change the PLL values to acheive
+ * any frequency changes. This is really only need by devices like the
+ * S3C2410 where there is no or limited divider between the PLL and the
+ * ARMCLK.
+ * @resume_clocks: Update the clocks on resume.
+ * @get_iotiming: Get the current IO timing data, mainly for use at start.
+ * @set_iotiming: Update the IO timings from the cached copies calculated
+ * from the @calc_iotiming entry when changing the frequency.
+ * @calc_iotiming: Calculate and update the cached copies of the IO timings
+ * from the newly calculated frequencies.
+ * @calc_freqtable: Calculate (fill in) the given frequency table from the
+ * current frequency configuration. If the table passed in is NULL,
+ * then the return is the number of elements to be filled for allocation
+ * of the table.
+ * @set_refresh: Set the memory refresh configuration.
+ * @set_fvco: Set the PLL frequencies.
+ * @set_divs: Update the clock divisors.
+ * @calc_divs: Calculate the clock divisors.
+ */
+struct s3c_cpufreq_info {
+ const char *name;
+ struct s3c_freq max;
+
+ unsigned int latency;
+
+ unsigned int locktime_m;
+ unsigned int locktime_u;
+ unsigned char locktime_bits;
+
+ unsigned int need_pll:1;
+
+ /* driver routines */
+
+ void (*resume_clocks)(void);
+
+ int (*get_iotiming)(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *timings);
+
+ void (*set_iotiming)(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *timings);
+
+ int (*calc_iotiming)(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *timings);
+
+ int (*calc_freqtable)(struct s3c_cpufreq_config *cfg,
+ struct cpufreq_frequency_table *t,
+ size_t table_size);
+
+ void (*debug_io_show)(struct seq_file *seq,
+ struct s3c_cpufreq_config *cfg,
+ union s3c_iobank *iob);
+
+ void (*set_refresh)(struct s3c_cpufreq_config *cfg);
+ void (*set_fvco)(struct s3c_cpufreq_config *cfg);
+ void (*set_divs)(struct s3c_cpufreq_config *cfg);
+ int (*calc_divs)(struct s3c_cpufreq_config *cfg);
+};
+
+extern int s3c_cpufreq_register(struct s3c_cpufreq_info *info);
+
+extern int s3c_plltab_register(struct cpufreq_frequency_table *plls, unsigned int plls_no);
+
+/* exports and utilities for debugfs */
+extern struct s3c_cpufreq_config *s3c_cpufreq_getconfig(void);
+extern struct s3c_iotimings *s3c_cpufreq_getiotimings(void);
+
+extern void s3c2410_iotiming_debugfs(struct seq_file *seq,
+ struct s3c_cpufreq_config *cfg,
+ union s3c_iobank *iob);
+
+extern void s3c2412_iotiming_debugfs(struct seq_file *seq,
+ struct s3c_cpufreq_config *cfg,
+ union s3c_iobank *iob);
+
+#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUGFS
+#define s3c_cpufreq_debugfs_call(x) x
+#else
+#define s3c_cpufreq_debugfs_call(x) NULL
+#endif
+
+/* Useful utility functions. */
+
+extern struct clk *s3c_cpufreq_clk_get(struct device *, const char *);
+
+/* S3C2410 and compatible exported functions */
+
+extern void s3c2410_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg);
+
+extern int s3c2410_iotiming_calc(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *iot);
+
+extern int s3c2410_iotiming_get(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *timings);
+
+extern void s3c2410_iotiming_set(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *iot);
+
+extern void s3c2410_set_fvco(struct s3c_cpufreq_config *cfg);
+
+/* S3C2412 compatible routines */
+
+extern int s3c2412_iotiming_get(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *timings);
+
+extern int s3c2412_iotiming_get(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *timings);
+
+extern int s3c2412_iotiming_calc(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *iot);
+
+extern void s3c2412_iotiming_set(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *iot);
+
+#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUG
+#define s3c_freq_dbg(x...) printk(KERN_INFO x)
+#else
+#define s3c_freq_dbg(x...) do { if (0) printk(x); } while (0)
+#endif /* CONFIG_CPU_FREQ_S3C24XX_DEBUG */
+
+#ifdef CONFIG_CPU_FREQ_S3C24XX_IODEBUG
+#define s3c_freq_iodbg(x...) printk(KERN_INFO x)
+#else
+#define s3c_freq_iodbg(x...) do { if (0) printk(x); } while (0)
+#endif /* CONFIG_CPU_FREQ_S3C24XX_IODEBUG */
+
+static inline int s3c_cpufreq_addfreq(struct cpufreq_frequency_table *table,
+ int index, size_t table_size,
+ unsigned int freq)
+{
+ if (index < 0)
+ return index;
+
+ if (table) {
+ if (index >= table_size)
+ return -ENOMEM;
+
+ s3c_freq_dbg("%s: { %d = %u kHz }\n",
+ __func__, index, freq);
+
+ table[index].index = index;
+ table[index].frequency = freq;
+ }
+
+ return index + 1;
+}
diff --git a/arch/arm/plat-s3c24xx/include/plat/fiq.h b/arch/arm/plat-s3c24xx/include/plat/fiq.h
new file mode 100644
index 00000000000..8521b8372c5
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/include/plat/fiq.h
@@ -0,0 +1,13 @@
+/* linux/include/asm-arm/plat-s3c24xx/fiq.h
+ *
+ * Copyright (c) 2009 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Header file for S3C24XX CPU FIQ support
+ *
+ * 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.
+*/
+
+extern int s3c24xx_set_fiq(unsigned int irq, bool on);
diff --git a/arch/arm/plat-s3c24xx/include/plat/s3c2410.h b/arch/arm/plat-s3c24xx/include/plat/s3c2410.h
index a9ac9e29759..b6deeef8f66 100644
--- a/arch/arm/plat-s3c24xx/include/plat/s3c2410.h
+++ b/arch/arm/plat-s3c24xx/include/plat/s3c2410.h
@@ -14,6 +14,7 @@
#ifdef CONFIG_CPU_S3C2410
extern int s3c2410_init(void);
+extern int s3c2410a_init(void);
extern void s3c2410_map_io(void);
diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c
index 958737775ad..d02f5f02045 100644
--- a/arch/arm/plat-s3c24xx/irq.c
+++ b/arch/arm/plat-s3c24xx/irq.c
@@ -493,6 +493,38 @@ s3c_irq_demux_extint4t7(unsigned int irq,
}
}
+#ifdef CONFIG_FIQ
+/**
+ * s3c24xx_set_fiq - set the FIQ routing
+ * @irq: IRQ number to route to FIQ on processor.
+ * @on: Whether to route @irq to the FIQ, or to remove the FIQ routing.
+ *
+ * Change the state of the IRQ to FIQ routing depending on @irq and @on. If
+ * @on is true, the @irq is checked to see if it can be routed and the
+ * interrupt controller updated to route the IRQ. If @on is false, the FIQ
+ * routing is cleared, regardless of which @irq is specified.
+ */
+int s3c24xx_set_fiq(unsigned int irq, bool on)
+{
+ u32 intmod;
+ unsigned offs;
+
+ if (on) {
+ offs = irq - FIQ_START;
+ if (offs > 31)
+ return -EINVAL;
+
+ intmod = 1 << offs;
+ } else {
+ intmod = 0;
+ }
+
+ __raw_writel(intmod, S3C2410_INTMOD);
+ return 0;
+}
+#endif
+
+
/* s3c24xx_init_irq
*
* Initialise S3C2410 IRQ system
@@ -505,6 +537,10 @@ void __init s3c24xx_init_irq(void)
int irqno;
int i;
+#ifdef CONFIG_FIQ
+ init_FIQ();
+#endif
+
irqdbf("s3c2410_init_irq: clearing interrupt status flags\n");
/* first, clear all interrupts pending... */
diff --git a/arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c b/arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c
new file mode 100644
index 00000000000..43ea80190d8
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c
@@ -0,0 +1,64 @@
+/* linux/arch/arm/plat-s3c24xx/s3c2410-cpufreq-utils.c
+ *
+ * Copyright (c) 2009 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX CPU Frequency scaling - utils for S3C2410/S3C2440/S3C2442
+ *
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/cpufreq.h>
+#include <linux/io.h>
+
+#include <mach/map.h>
+#include <mach/regs-mem.h>
+#include <mach/regs-clock.h>
+
+#include <plat/cpu-freq-core.h>
+
+/**
+ * s3c2410_cpufreq_setrefresh - set SDRAM refresh value
+ * @cfg: The frequency configuration
+ *
+ * Set the SDRAM refresh value appropriately for the configured
+ * frequency.
+ */
+void s3c2410_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg)
+{
+ struct s3c_cpufreq_board *board = cfg->board;
+ unsigned long refresh;
+ unsigned long refval;
+
+ /* Reduce both the refresh time (in ns) and the frequency (in MHz)
+ * down to ensure that we do not overflow 32 bit numbers.
+ *
+ * This should work for HCLK up to 133MHz and refresh period up
+ * to 30usec.
+ */
+
+ refresh = (cfg->freq.hclk / 100) * (board->refresh / 10);
+ refresh = DIV_ROUND_UP(refresh, (1000 * 1000)); /* apply scale */
+ refresh = (1 << 11) + 1 - refresh;
+
+ s3c_freq_dbg("%s: refresh value %lu\n", __func__, refresh);
+
+ refval = __raw_readl(S3C2410_REFRESH);
+ refval &= ~((1 << 12) - 1);
+ refval |= refresh;
+ __raw_writel(refval, S3C2410_REFRESH);
+}
+
+/**
+ * s3c2410_set_fvco - set the PLL value
+ * @cfg: The frequency configuration
+ */
+void s3c2410_set_fvco(struct s3c_cpufreq_config *cfg)
+{
+ __raw_writel(cfg->pll.index, S3C2410_MPLLCON);
+}
diff --git a/arch/arm/plat-s3c24xx/s3c2410-iotiming.c b/arch/arm/plat-s3c24xx/s3c2410-iotiming.c
new file mode 100644
index 00000000000..d0a3a145cd4
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2410-iotiming.c
@@ -0,0 +1,477 @@
+/* linux/arch/arm/plat-s3c24xx/s3c2410-iotiming.c
+ *
+ * Copyright (c) 2006,2008,2009 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX CPU Frequency scaling - IO timing for S3C2410/S3C2440/S3C2442
+ *
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/cpufreq.h>
+#include <linux/seq_file.h>
+#include <linux/io.h>
+
+#include <mach/map.h>
+#include <mach/regs-mem.h>
+#include <mach/regs-clock.h>
+
+#include <plat/cpu-freq-core.h>
+
+#define print_ns(x) ((x) / 10), ((x) % 10)
+
+/**
+ * s3c2410_print_timing - print bank timing data for debug purposes
+ * @pfx: The prefix to put on the output
+ * @timings: The timing inforamtion to print.
+*/
+static void s3c2410_print_timing(const char *pfx,
+ struct s3c_iotimings *timings)
+{
+ struct s3c2410_iobank_timing *bt;
+ int bank;
+
+ for (bank = 0; bank < MAX_BANKS; bank++) {
+ bt = timings->bank[bank].io_2410;
+ if (!bt)
+ continue;
+
+ printk(KERN_DEBUG "%s %d: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, "
+ "Tcoh=%d.%d, Tcah=%d.%d\n", pfx, bank,
+ print_ns(bt->tacs),
+ print_ns(bt->tcos),
+ print_ns(bt->tacc),
+ print_ns(bt->tcoh),
+ print_ns(bt->tcah));
+ }
+}
+
+/**
+ * bank_reg - convert bank number to pointer to the control register.
+ * @bank: The IO bank number.
+ */
+static inline void __iomem *bank_reg(unsigned int bank)
+{
+ return S3C2410_BANKCON0 + (bank << 2);
+}
+
+/**
+ * bank_is_io - test whether bank is used for IO
+ * @bankcon: The bank control register.
+ *
+ * This is a simplistic test to see if any BANKCON[x] is not an IO
+ * bank. It currently does not take into account whether BWSCON has
+ * an illegal width-setting in it, or if the pin connected to nCS[x]
+ * is actually being handled as a chip-select.
+ */
+static inline int bank_is_io(unsigned long bankcon)
+{
+ return !(bankcon & S3C2410_BANKCON_SDRAM);
+}
+
+/**
+ * to_div - convert cycle time to divisor
+ * @cyc: The cycle time, in 10ths of nanoseconds.
+ * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
+ *
+ * Convert the given cycle time into the divisor to use to obtain it from
+ * HCLK.
+*/
+static inline unsigned int to_div(unsigned int cyc, unsigned int hclk_tns)
+{
+ if (cyc == 0)
+ return 0;
+
+ return DIV_ROUND_UP(cyc, hclk_tns);
+}
+
+/**
+ * calc_0124 - calculate divisor control for divisors that do /0, /1. /2 and /4
+ * @cyc: The cycle time, in 10ths of nanoseconds.
+ * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
+ * @v: Pointer to register to alter.
+ * @shift: The shift to get to the control bits.
+ *
+ * Calculate the divisor, and turn it into the correct control bits to
+ * set in the result, @v.
+ */
+static unsigned int calc_0124(unsigned int cyc, unsigned long hclk_tns,
+ unsigned long *v, int shift)
+{
+ unsigned int div = to_div(cyc, hclk_tns);
+ unsigned long val;
+
+ s3c_freq_iodbg("%s: cyc=%d, hclk=%lu, shift=%d => div %d\n",
+ __func__, cyc, hclk_tns, shift, div);
+
+ switch (div) {
+ case 0:
+ val = 0;
+ break;
+ case 1:
+ val = 1;
+ break;
+ case 2:
+ val = 2;
+ break;
+ case 3:
+ case 4:
+ val = 3;
+ break;
+ default:
+ return -1;
+ }
+
+ *v |= val << shift;
+ return 0;
+}
+
+int calc_tacp(unsigned int cyc, unsigned long hclk, unsigned long *v)
+{
+ /* Currently no support for Tacp calculations. */
+ return 0;
+}
+
+/**
+ * calc_tacc - calculate divisor control for tacc.
+ * @cyc: The cycle time, in 10ths of nanoseconds.
+ * @nwait_en: IS nWAIT enabled for this bank.
+ * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
+ * @v: Pointer to register to alter.
+ *
+ * Calculate the divisor control for tACC, taking into account whether
+ * the bank has nWAIT enabled. The result is used to modify the value
+ * pointed to by @v.
+*/
+static int calc_tacc(unsigned int cyc, int nwait_en,
+ unsigned long hclk_tns, unsigned long *v)
+{
+ unsigned int div = to_div(cyc, hclk_tns);
+ unsigned long val;
+
+ s3c_freq_iodbg("%s: cyc=%u, nwait=%d, hclk=%lu => div=%u\n",
+ __func__, cyc, nwait_en, hclk_tns, div);
+
+ /* if nWait enabled on an bank, Tacc must be at-least 4 cycles. */
+ if (nwait_en && div < 4)
+ div = 4;
+
+ switch (div) {
+ case 0:
+ val = 0;
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ val = div - 1;
+ break;
+
+ case 5:
+ case 6:
+ val = 4;
+ break;
+
+ case 7:
+ case 8:
+ val = 5;
+ break;
+
+ case 9:
+ case 10:
+ val = 6;
+ break;
+
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ val = 7;
+ break;
+
+ default:
+ return -1;
+ }
+
+ *v |= val << 8;
+ return 0;
+}
+
+/**
+ * s3c2410_calc_bank - calculate bank timing infromation
+ * @cfg: The configuration we need to calculate for.
+ * @bt: The bank timing information.
+ *
+ * Given the cycle timine for a bank @bt, calculate the new BANKCON
+ * setting for the @cfg timing. This updates the timing information
+ * ready for the cpu frequency change.
+ */
+static int s3c2410_calc_bank(struct s3c_cpufreq_config *cfg,
+ struct s3c2410_iobank_timing *bt)
+{
+ unsigned long hclk = cfg->freq.hclk_tns;
+ unsigned long res;
+ int ret;
+
+ res = bt->bankcon;
+ res &= (S3C2410_BANKCON_SDRAM | S3C2410_BANKCON_PMC16);
+
+ /* tacp: 2,3,4,5 */
+ /* tcah: 0,1,2,4 */
+ /* tcoh: 0,1,2,4 */
+ /* tacc: 1,2,3,4,6,7,10,14 (>4 for nwait) */
+ /* tcos: 0,1,2,4 */
+ /* tacs: 0,1,2,4 */
+
+ ret = calc_0124(bt->tacs, hclk, &res, S3C2410_BANKCON_Tacs_SHIFT);
+ ret |= calc_0124(bt->tcos, hclk, &res, S3C2410_BANKCON_Tcos_SHIFT);
+ ret |= calc_0124(bt->tcah, hclk, &res, S3C2410_BANKCON_Tcah_SHIFT);
+ ret |= calc_0124(bt->tcoh, hclk, &res, S3C2410_BANKCON_Tcoh_SHIFT);
+
+ if (ret)
+ return -EINVAL;
+
+ ret |= calc_tacp(bt->tacp, hclk, &res);
+ ret |= calc_tacc(bt->tacc, bt->nwait_en, hclk, &res);
+
+ if (ret)
+ return -EINVAL;
+
+ bt->bankcon = res;
+ return 0;
+}
+
+static unsigned int tacc_tab[] = {
+ [0] = 1,
+ [1] = 2,
+ [2] = 3,
+ [3] = 4,
+ [4] = 6,
+ [5] = 9,
+ [6] = 10,
+ [7] = 14,
+};
+
+/**
+ * get_tacc - turn tACC value into cycle time
+ * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
+ * @val: The bank timing register value, shifed down.
+ */
+static unsigned int get_tacc(unsigned long hclk_tns,
+ unsigned long val)
+{
+ val &= 7;
+ return hclk_tns * tacc_tab[val];
+}
+
+/**
+ * get_0124 - turn 0/1/2/4 divider into cycle time
+ * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
+ * @val: The bank timing register value, shifed down.
+ */
+static unsigned int get_0124(unsigned long hclk_tns,
+ unsigned long val)
+{
+ val &= 3;
+ return hclk_tns * ((val == 3) ? 4 : val);
+}
+
+/**
+ * s3c2410_iotiming_getbank - turn BANKCON into cycle time information
+ * @cfg: The frequency configuration
+ * @bt: The bank timing to fill in (uses cached BANKCON)
+ *
+ * Given the BANKCON setting in @bt and the current frequency settings
+ * in @cfg, update the cycle timing information.
+ */
+void s3c2410_iotiming_getbank(struct s3c_cpufreq_config *cfg,
+ struct s3c2410_iobank_timing *bt)
+{
+ unsigned long bankcon = bt->bankcon;
+ unsigned long hclk = cfg->freq.hclk_tns;
+
+ bt->tcah = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcah_SHIFT);
+ bt->tcoh = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcoh_SHIFT);
+ bt->tcos = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcos_SHIFT);
+ bt->tacs = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tacs_SHIFT);
+ bt->tacc = get_tacc(hclk, bankcon >> S3C2410_BANKCON_Tacc_SHIFT);
+}
+
+/**
+ * s3c2410_iotiming_debugfs - debugfs show io bank timing information
+ * @seq: The seq_file to write output to using seq_printf().
+ * @cfg: The current configuration.
+ * @iob: The IO bank information to decode.
+ */
+void s3c2410_iotiming_debugfs(struct seq_file *seq,
+ struct s3c_cpufreq_config *cfg,
+ union s3c_iobank *iob)
+{
+ struct s3c2410_iobank_timing *bt = iob->io_2410;
+ unsigned long bankcon = bt->bankcon;
+ unsigned long hclk = cfg->freq.hclk_tns;
+ unsigned int tacs;
+ unsigned int tcos;
+ unsigned int tacc;
+ unsigned int tcoh;
+ unsigned int tcah;
+
+ seq_printf(seq, "BANKCON=0x%08lx\n", bankcon);
+
+ tcah = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcah_SHIFT);
+ tcoh = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcoh_SHIFT);
+ tcos = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcos_SHIFT);
+ tacs = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tacs_SHIFT);
+ tacc = get_tacc(hclk, bankcon >> S3C2410_BANKCON_Tacc_SHIFT);
+
+ seq_printf(seq,
+ "\tRead: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, Tcoh=%d.%d, Tcah=%d.%d\n",
+ print_ns(bt->tacs),
+ print_ns(bt->tcos),
+ print_ns(bt->tacc),
+ print_ns(bt->tcoh),
+ print_ns(bt->tcah));
+
+ seq_printf(seq,
+ "\t Set: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, Tcoh=%d.%d, Tcah=%d.%d\n",
+ print_ns(tacs),
+ print_ns(tcos),
+ print_ns(tacc),
+ print_ns(tcoh),
+ print_ns(tcah));
+}
+
+/**
+ * s3c2410_iotiming_calc - Calculate bank timing for frequency change.
+ * @cfg: The frequency configuration
+ * @iot: The IO timing information to fill out.
+ *
+ * Calculate the new values for the banks in @iot based on the new
+ * frequency information in @cfg. This is then used by s3c2410_iotiming_set()
+ * to update the timing when necessary.
+ */
+int s3c2410_iotiming_calc(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *iot)
+{
+ struct s3c2410_iobank_timing *bt;
+ unsigned long bankcon;
+ int bank;
+ int ret;
+
+ for (bank = 0; bank < MAX_BANKS; bank++) {
+ bankcon = __raw_readl(bank_reg(bank));
+ bt = iot->bank[bank].io_2410;
+
+ if (!bt)
+ continue;
+
+ bt->bankcon = bankcon;
+
+ ret = s3c2410_calc_bank(cfg, bt);
+ if (ret) {
+ printk(KERN_ERR "%s: cannot calculate bank %d io\n",
+ __func__, bank);
+ goto err;
+ }
+
+ s3c_freq_iodbg("%s: bank %d: con=%08lx\n",
+ __func__, bank, bt->bankcon);
+ }
+
+ return 0;
+ err:
+ return ret;
+}
+
+/**
+ * s3c2410_iotiming_set - set the IO timings from the given setup.
+ * @cfg: The frequency configuration
+ * @iot: The IO timing information to use.
+ *
+ * Set all the currently used IO bank timing information generated
+ * by s3c2410_iotiming_calc() once the core has validated that all
+ * the new values are within permitted bounds.
+ */
+void s3c2410_iotiming_set(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *iot)
+{
+ struct s3c2410_iobank_timing *bt;
+ int bank;
+
+ /* set the io timings from the specifier */
+
+ for (bank = 0; bank < MAX_BANKS; bank++) {
+ bt = iot->bank[bank].io_2410;
+ if (!bt)
+ continue;
+
+ __raw_writel(bt->bankcon, bank_reg(bank));
+ }
+}
+
+/**
+ * s3c2410_iotiming_get - Get the timing information from current registers.
+ * @cfg: The frequency configuration
+ * @timings: The IO timing information to fill out.
+ *
+ * Calculate the @timings timing information from the current frequency
+ * information in @cfg, and the new frequency configur
+ * through all the IO banks, reading the state and then updating @iot
+ * as necessary.
+ *
+ * This is used at the moment on initialisation to get the current
+ * configuration so that boards do not have to carry their own setup
+ * if the timings are correct on initialisation.
+ */
+
+int s3c2410_iotiming_get(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *timings)
+{
+ struct s3c2410_iobank_timing *bt;
+ unsigned long bankcon;
+ unsigned long bwscon;
+ int bank;
+
+ bwscon = __raw_readl(S3C2410_BWSCON);
+
+ /* look through all banks to see what is currently set. */
+
+ for (bank = 0; bank < MAX_BANKS; bank++) {
+ bankcon = __raw_readl(bank_reg(bank));
+
+ if (!bank_is_io(bankcon))
+ continue;
+
+ s3c_freq_iodbg("%s: bank %d: con %08lx\n",
+ __func__, bank, bankcon);
+
+ bt = kzalloc(sizeof(struct s3c2410_iobank_timing), GFP_KERNEL);
+ if (!bt) {
+ printk(KERN_ERR "%s: no memory for bank\n", __func__);
+ return -ENOMEM;
+ }
+
+ /* find out in nWait is enabled for bank. */
+
+ if (bank != 0) {
+ unsigned long tmp = S3C2410_BWSCON_GET(bwscon, bank);
+ if (tmp & S3C2410_BWSCON_WS)
+ bt->nwait_en = 1;
+ }
+
+ timings->bank[bank].io_2410 = bt;
+ bt->bankcon = bankcon;
+
+ s3c2410_iotiming_getbank(cfg, bt);
+ }
+
+ s3c2410_print_timing("get", timings);
+ return 0;
+}
diff --git a/arch/arm/plat-s3c24xx/s3c2412-iotiming.c b/arch/arm/plat-s3c24xx/s3c2412-iotiming.c
new file mode 100644
index 00000000000..fd45e47facb
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2412-iotiming.c
@@ -0,0 +1,285 @@
+/* linux/arch/arm/plat-s3c24xx/s3c2412-iotiming.c
+ *
+ * Copyright (c) 2006,2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C2412/S3C2443 (PL093 based) IO timing support
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/cpufreq.h>
+#include <linux/seq_file.h>
+#include <linux/sysdev.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <linux/amba/pl093.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/regs-s3c2412-mem.h>
+
+#include <plat/cpu.h>
+#include <plat/cpu-freq-core.h>
+#include <plat/clock.h>
+
+#define print_ns(x) ((x) / 10), ((x) % 10)
+
+/**
+ * s3c2412_print_timing - print timing infromation via printk.
+ * @pfx: The prefix to print each line with.
+ * @iot: The IO timing information
+ */
+static void s3c2412_print_timing(const char *pfx, struct s3c_iotimings *iot)
+{
+ struct s3c2412_iobank_timing *bt;
+ unsigned int bank;
+
+ for (bank = 0; bank < MAX_BANKS; bank++) {
+ bt = iot->bank[bank].io_2412;
+ if (!bt)
+ continue;
+
+ printk(KERN_DEBUG "%s: %d: idcy=%d.%d wstrd=%d.%d wstwr=%d,%d"
+ "wstoen=%d.%d wstwen=%d.%d wstbrd=%d.%d\n", pfx, bank,
+ print_ns(bt->idcy),
+ print_ns(bt->wstrd),
+ print_ns(bt->wstwr),
+ print_ns(bt->wstoen),
+ print_ns(bt->wstwen),
+ print_ns(bt->wstbrd));
+ }
+}
+
+/**
+ * to_div - turn a cycle length into a divisor setting.
+ * @cyc_tns: The cycle time in 10ths of nanoseconds.
+ * @clk_tns: The clock period in 10ths of nanoseconds.
+ */
+static inline unsigned int to_div(unsigned int cyc_tns, unsigned int clk_tns)
+{
+ return cyc_tns ? DIV_ROUND_UP(cyc_tns, clk_tns) : 0;
+}
+
+/**
+ * calc_timing - calculate timing divisor value and check in range.
+ * @hwtm: The hardware timing in 10ths of nanoseconds.
+ * @clk_tns: The clock period in 10ths of nanoseconds.
+ * @err: Pointer to err variable to update in event of failure.
+ */
+static unsigned int calc_timing(unsigned int hwtm, unsigned int clk_tns,
+ unsigned int *err)
+{
+ unsigned int ret = to_div(hwtm, clk_tns);
+
+ if (ret > 0xf)
+ *err = -EINVAL;
+
+ return ret;
+}
+
+/**
+ * s3c2412_calc_bank - calculate the bank divisor settings.
+ * @cfg: The current frequency configuration.
+ * @bt: The bank timing.
+ */
+static int s3c2412_calc_bank(struct s3c_cpufreq_config *cfg,
+ struct s3c2412_iobank_timing *bt)
+{
+ unsigned int hclk = cfg->freq.hclk_tns;
+ int err = 0;
+
+ bt->smbidcyr = calc_timing(bt->idcy, hclk, &err);
+ bt->smbwstrd = calc_timing(bt->wstrd, hclk, &err);
+ bt->smbwstwr = calc_timing(bt->wstwr, hclk, &err);
+ bt->smbwstoen = calc_timing(bt->wstoen, hclk, &err);
+ bt->smbwstwen = calc_timing(bt->wstwen, hclk, &err);
+ bt->smbwstbrd = calc_timing(bt->wstbrd, hclk, &err);
+
+ return err;
+}
+
+/**
+ * s3c2412_iotiming_debugfs - debugfs show io bank timing information
+ * @seq: The seq_file to write output to using seq_printf().
+ * @cfg: The current configuration.
+ * @iob: The IO bank information to decode.
+*/
+void s3c2412_iotiming_debugfs(struct seq_file *seq,
+ struct s3c_cpufreq_config *cfg,
+ union s3c_iobank *iob)
+{
+ struct s3c2412_iobank_timing *bt = iob->io_2412;
+
+ seq_printf(seq,
+ "\tRead: idcy=%d.%d wstrd=%d.%d wstwr=%d,%d"
+ "wstoen=%d.%d wstwen=%d.%d wstbrd=%d.%d\n",
+ print_ns(bt->idcy),
+ print_ns(bt->wstrd),
+ print_ns(bt->wstwr),
+ print_ns(bt->wstoen),
+ print_ns(bt->wstwen),
+ print_ns(bt->wstbrd));
+}
+
+/**
+ * s3c2412_iotiming_calc - calculate all the bank divisor settings.
+ * @cfg: The current frequency configuration.
+ * @iot: The bank timing information.
+ *
+ * Calculate the timing information for all the banks that are
+ * configured as IO, using s3c2412_calc_bank().
+ */
+int s3c2412_iotiming_calc(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *iot)
+{
+ struct s3c2412_iobank_timing *bt;
+ int bank;
+ int ret;
+
+ for (bank = 0; bank < MAX_BANKS; bank++) {
+ bt = iot->bank[bank].io_2412;
+ if (!bt)
+ continue;
+
+ ret = s3c2412_calc_bank(cfg, bt);
+ if (ret) {
+ printk(KERN_ERR "%s: cannot calculate bank %d io\n",
+ __func__, bank);
+ goto err;
+ }
+ }
+
+ return 0;
+ err:
+ return ret;
+}
+
+/**
+ * s3c2412_iotiming_set - set the timing information
+ * @cfg: The current frequency configuration.
+ * @iot: The bank timing information.
+ *
+ * Set the IO bank information from the details calculated earlier from
+ * calling s3c2412_iotiming_calc().
+ */
+void s3c2412_iotiming_set(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *iot)
+{
+ struct s3c2412_iobank_timing *bt;
+ void __iomem *regs;
+ int bank;
+
+ /* set the io timings from the specifier */
+
+ for (bank = 0; bank < MAX_BANKS; bank++) {
+ bt = iot->bank[bank].io_2412;
+ if (!bt)
+ continue;
+
+ regs = S3C2412_SSMC_BANK(bank);
+
+ __raw_writel(bt->smbidcyr, regs + SMBIDCYR);
+ __raw_writel(bt->smbwstrd, regs + SMBWSTRDR);
+ __raw_writel(bt->smbwstwr, regs + SMBWSTWRR);
+ __raw_writel(bt->smbwstoen, regs + SMBWSTOENR);
+ __raw_writel(bt->smbwstwen, regs + SMBWSTWENR);
+ __raw_writel(bt->smbwstbrd, regs + SMBWSTBRDR);
+ }
+}
+
+static inline unsigned int s3c2412_decode_timing(unsigned int clock, u32 reg)
+{
+ return (reg & 0xf) * clock;
+}
+
+static void s3c2412_iotiming_getbank(struct s3c_cpufreq_config *cfg,
+ struct s3c2412_iobank_timing *bt,
+ unsigned int bank)
+{
+ unsigned long clk = cfg->freq.hclk_tns; /* ssmc clock??? */
+ void __iomem *regs = S3C2412_SSMC_BANK(bank);
+
+ bt->idcy = s3c2412_decode_timing(clk, __raw_readl(regs + SMBIDCYR));
+ bt->wstrd = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTRDR));
+ bt->wstoen = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTOENR));
+ bt->wstwen = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTWENR));
+ bt->wstbrd = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTBRDR));
+}
+
+/**
+ * bank_is_io - return true if bank is (possibly) IO.
+ * @bank: The bank number.
+ * @bankcfg: The value of S3C2412_EBI_BANKCFG.
+ */
+static inline bool bank_is_io(unsigned int bank, u32 bankcfg)
+{
+ if (bank < 2)
+ return true;
+
+ return !(bankcfg & (1 << bank));
+}
+
+int s3c2412_iotiming_get(struct s3c_cpufreq_config *cfg,
+ struct s3c_iotimings *timings)
+{
+ struct s3c2412_iobank_timing *bt;
+ u32 bankcfg = __raw_readl(S3C2412_EBI_BANKCFG);
+ unsigned int bank;
+
+ /* look through all banks to see what is currently set. */
+
+ for (bank = 0; bank < MAX_BANKS; bank++) {
+ if (!bank_is_io(bank, bankcfg))
+ continue;
+
+ bt = kzalloc(sizeof(struct s3c2412_iobank_timing), GFP_KERNEL);
+ if (!bt) {
+ printk(KERN_ERR "%s: no memory for bank\n", __func__);
+ return -ENOMEM;
+ }
+
+ timings->bank[bank].io_2412 = bt;
+ s3c2412_iotiming_getbank(cfg, bt, bank);
+ }
+
+ s3c2412_print_timing("get", timings);
+ return 0;
+}
+
+/* this is in here as it is so small, it doesn't currently warrant a file
+ * to itself. We expect that any s3c24xx needing this is going to also
+ * need the iotiming support.
+ */
+void s3c2412_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg)
+{
+ struct s3c_cpufreq_board *board = cfg->board;
+ u32 refresh;
+
+ WARN_ON(board == NULL);
+
+ /* Reduce both the refresh time (in ns) and the frequency (in MHz)
+ * down to ensure that we do not overflow 32 bit numbers.
+ *
+ * This should work for HCLK up to 133MHz and refresh period up
+ * to 30usec.
+ */
+
+ refresh = (cfg->freq.hclk / 100) * (board->refresh / 10);
+ refresh = DIV_ROUND_UP(refresh, (1000 * 1000)); /* apply scale */
+ refresh &= ((1 << 16) - 1);
+
+ s3c_freq_dbg("%s: refresh value %u\n", __func__, (unsigned int)refresh);
+
+ __raw_writel(refresh, S3C2412_REFRESH);
+}
diff --git a/arch/arm/plat-s3c24xx/s3c2440-cpufreq.c b/arch/arm/plat-s3c24xx/s3c2440-cpufreq.c
new file mode 100644
index 00000000000..ae2e6c604f2
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2440-cpufreq.c
@@ -0,0 +1,311 @@
+/* linux/arch/arm/plat-s3c24xx/s3c2440-cpufreq.c
+ *
+ * Copyright (c) 2006,2008,2009 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ * Vincent Sanders <vince@simtec.co.uk>
+ *
+ * S3C2440/S3C2442 CPU Frequency scaling
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/cpufreq.h>
+#include <linux/sysdev.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/regs-clock.h>
+
+#include <plat/cpu.h>
+#include <plat/cpu-freq-core.h>
+#include <plat/clock.h>
+
+static struct clk *xtal;
+static struct clk *fclk;
+static struct clk *hclk;
+static struct clk *armclk;
+
+/* HDIV: 1, 2, 3, 4, 6, 8 */
+
+static inline int within_khz(unsigned long a, unsigned long b)
+{
+ long diff = a - b;
+
+ return (diff >= -1000 && diff <= 1000);
+}
+
+/**
+ * s3c2440_cpufreq_calcdivs - calculate divider settings
+ * @cfg: The cpu frequency settings.
+ *
+ * Calcualte the divider values for the given frequency settings
+ * specified in @cfg. The values are stored in @cfg for later use
+ * by the relevant set routine if the request settings can be reached.
+ */
+int s3c2440_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
+{
+ unsigned int hdiv, pdiv;
+ unsigned long hclk, fclk, armclk;
+ unsigned long hclk_max;
+
+ fclk = cfg->freq.fclk;
+ armclk = cfg->freq.armclk;
+ hclk_max = cfg->max.hclk;
+
+ s3c_freq_dbg("%s: fclk is %lu, armclk %lu, max hclk %lu\n",
+ __func__, fclk, armclk, hclk_max);
+
+ if (armclk > fclk) {
+ printk(KERN_WARNING "%s: armclk > fclk\n", __func__);
+ armclk = fclk;
+ }
+
+ /* if we are in DVS, we need HCLK to be <= ARMCLK */
+ if (armclk < fclk && armclk < hclk_max)
+ hclk_max = armclk;
+
+ for (hdiv = 1; hdiv < 9; hdiv++) {
+ if (hdiv == 5 || hdiv == 7)
+ hdiv++;
+
+ hclk = (fclk / hdiv);
+ if (hclk <= hclk_max || within_khz(hclk, hclk_max))
+ break;
+ }
+
+ s3c_freq_dbg("%s: hclk %lu, div %d\n", __func__, hclk, hdiv);
+
+ if (hdiv > 8)
+ goto invalid;
+
+ pdiv = (hclk > cfg->max.pclk) ? 2 : 1;
+
+ if ((hclk / pdiv) > cfg->max.pclk)
+ pdiv++;
+
+ s3c_freq_dbg("%s: pdiv %d\n", __func__, pdiv);
+
+ if (pdiv > 2)
+ goto invalid;
+
+ pdiv *= hdiv;
+
+ /* calculate a valid armclk */
+
+ if (armclk < hclk)
+ armclk = hclk;
+
+ /* if we're running armclk lower than fclk, this really means
+ * that the system should go into dvs mode, which means that
+ * armclk is connected to hclk. */
+ if (armclk < fclk) {
+ cfg->divs.dvs = 1;
+ armclk = hclk;
+ } else
+ cfg->divs.dvs = 0;
+
+ cfg->freq.armclk = armclk;
+
+ /* store the result, and then return */
+
+ cfg->divs.h_divisor = hdiv;
+ cfg->divs.p_divisor = pdiv;
+
+ return 0;
+
+ invalid:
+ return -EINVAL;
+}
+
+#define CAMDIVN_HCLK_HALF (S3C2440_CAMDIVN_HCLK3_HALF | \
+ S3C2440_CAMDIVN_HCLK4_HALF)
+
+/**
+ * s3c2440_cpufreq_setdivs - set the cpu frequency divider settings
+ * @cfg: The cpu frequency settings.
+ *
+ * Set the divisors from the settings in @cfg, which where generated
+ * during the calculation phase by s3c2440_cpufreq_calcdivs().
+ */
+static void s3c2440_cpufreq_setdivs(struct s3c_cpufreq_config *cfg)
+{
+ unsigned long clkdiv, camdiv;
+
+ s3c_freq_dbg("%s: divsiors: h=%d, p=%d\n", __func__,
+ cfg->divs.h_divisor, cfg->divs.p_divisor);
+
+ clkdiv = __raw_readl(S3C2410_CLKDIVN);
+ camdiv = __raw_readl(S3C2440_CAMDIVN);
+
+ clkdiv &= ~(S3C2440_CLKDIVN_HDIVN_MASK | S3C2440_CLKDIVN_PDIVN);
+ camdiv &= ~CAMDIVN_HCLK_HALF;
+
+ switch (cfg->divs.h_divisor) {
+ case 1:
+ clkdiv |= S3C2440_CLKDIVN_HDIVN_1;
+ break;
+
+ case 2:
+ clkdiv |= S3C2440_CLKDIVN_HDIVN_2;
+ break;
+
+ case 6:
+ camdiv |= S3C2440_CAMDIVN_HCLK3_HALF;
+ case 3:
+ clkdiv |= S3C2440_CLKDIVN_HDIVN_3_6;
+ break;
+
+ case 8:
+ camdiv |= S3C2440_CAMDIVN_HCLK4_HALF;
+ case 4:
+ clkdiv |= S3C2440_CLKDIVN_HDIVN_4_8;
+ break;
+
+ default:
+ BUG(); /* we don't expect to get here. */
+ }
+
+ if (cfg->divs.p_divisor != cfg->divs.h_divisor)
+ clkdiv |= S3C2440_CLKDIVN_PDIVN;
+
+ /* todo - set pclk. */
+
+ /* Write the divisors first with hclk intentionally halved so that
+ * when we write clkdiv we will under-frequency instead of over. We
+ * then make a short delay and remove the hclk halving if necessary.
+ */
+
+ __raw_writel(camdiv | CAMDIVN_HCLK_HALF, S3C2440_CAMDIVN);
+ __raw_writel(clkdiv, S3C2410_CLKDIVN);
+
+ ndelay(20);
+ __raw_writel(camdiv, S3C2440_CAMDIVN);
+
+ clk_set_parent(armclk, cfg->divs.dvs ? hclk : fclk);
+}
+
+static int run_freq_for(unsigned long max_hclk, unsigned long fclk,
+ int *divs,
+ struct cpufreq_frequency_table *table,
+ size_t table_size)
+{
+ unsigned long freq;
+ int index = 0;
+ int div;
+
+ for (div = *divs; div > 0; div = *divs++) {
+ freq = fclk / div;
+
+ if (freq > max_hclk && div != 1)
+ continue;
+
+ freq /= 1000; /* table is in kHz */
+ index = s3c_cpufreq_addfreq(table, index, table_size, freq);
+ if (index < 0)
+ break;
+ }
+
+ return index;
+}
+
+static int hclk_divs[] = { 1, 2, 3, 4, 6, 8, -1 };
+
+static int s3c2440_cpufreq_calctable(struct s3c_cpufreq_config *cfg,
+ struct cpufreq_frequency_table *table,
+ size_t table_size)
+{
+ int ret;
+
+ WARN_ON(cfg->info == NULL);
+ WARN_ON(cfg->board == NULL);
+
+ ret = run_freq_for(cfg->info->max.hclk,
+ cfg->info->max.fclk,
+ hclk_divs,
+ table, table_size);
+
+ s3c_freq_dbg("%s: returning %d\n", __func__, ret);
+
+ return ret;
+}
+
+struct s3c_cpufreq_info s3c2440_cpufreq_info = {
+ .max = {
+ .fclk = 400000000,
+ .hclk = 133333333,
+ .pclk = 66666666,
+ },
+
+ .locktime_m = 300,
+ .locktime_u = 300,
+ .locktime_bits = 16,
+
+ .name = "s3c244x",
+ .calc_iotiming = s3c2410_iotiming_calc,
+ .set_iotiming = s3c2410_iotiming_set,
+ .get_iotiming = s3c2410_iotiming_get,
+ .set_fvco = s3c2410_set_fvco,
+
+ .set_refresh = s3c2410_cpufreq_setrefresh,
+ .set_divs = s3c2440_cpufreq_setdivs,
+ .calc_divs = s3c2440_cpufreq_calcdivs,
+ .calc_freqtable = s3c2440_cpufreq_calctable,
+
+ .resume_clocks = s3c244x_setup_clocks,
+
+ .debug_io_show = s3c_cpufreq_debugfs_call(s3c2410_iotiming_debugfs),
+};
+
+static int s3c2440_cpufreq_add(struct sys_device *sysdev)
+{
+ xtal = s3c_cpufreq_clk_get(NULL, "xtal");
+ hclk = s3c_cpufreq_clk_get(NULL, "hclk");
+ fclk = s3c_cpufreq_clk_get(NULL, "fclk");
+ armclk = s3c_cpufreq_clk_get(NULL, "armclk");
+
+ if (IS_ERR(xtal) || IS_ERR(hclk) || IS_ERR(fclk) || IS_ERR(armclk)) {
+ printk(KERN_ERR "%s: failed to get clocks\n", __func__);
+ return -ENOENT;
+ }
+
+ return s3c_cpufreq_register(&s3c2440_cpufreq_info);
+}
+
+static struct sysdev_driver s3c2440_cpufreq_driver = {
+ .add = s3c2440_cpufreq_add,
+};
+
+static int s3c2440_cpufreq_init(void)
+{
+ return sysdev_driver_register(&s3c2440_sysclass,
+ &s3c2440_cpufreq_driver);
+}
+
+/* arch_initcall adds the clocks we need, so use subsys_initcall. */
+subsys_initcall(s3c2440_cpufreq_init);
+
+static struct sysdev_driver s3c2442_cpufreq_driver = {
+ .add = s3c2440_cpufreq_add,
+};
+
+static int s3c2442_cpufreq_init(void)
+{
+ return sysdev_driver_register(&s3c2442_sysclass,
+ &s3c2442_cpufreq_driver);
+}
+
+subsys_initcall(s3c2442_cpufreq_init);
diff --git a/arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c b/arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c
new file mode 100644
index 00000000000..ff9443b233a
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c
@@ -0,0 +1,97 @@
+/* arch/arm/plat-s3c24xx/s3c2440-pll-12000000.c
+ *
+ * Copyright (c) 2006,2007 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ * Vincent Sanders <vince@arm.linux.org.uk>
+ *
+ * S3C2440/S3C2442 CPU PLL tables (12MHz Crystal)
+ *
+ * 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/types.h>
+#include <linux/kernel.h>
+#include <linux/sysdev.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <plat/cpu.h>
+#include <plat/cpu-freq-core.h>
+
+static struct cpufreq_frequency_table s3c2440_plls_12[] __initdata = {
+ { .frequency = 75000000, .index = PLLVAL(0x75, 3, 3), }, /* FVco 600.000000 */
+ { .frequency = 80000000, .index = PLLVAL(0x98, 4, 3), }, /* FVco 640.000000 */
+ { .frequency = 90000000, .index = PLLVAL(0x70, 2, 3), }, /* FVco 720.000000 */
+ { .frequency = 100000000, .index = PLLVAL(0x5c, 1, 3), }, /* FVco 800.000000 */
+ { .frequency = 110000000, .index = PLLVAL(0x66, 1, 3), }, /* FVco 880.000000 */
+ { .frequency = 120000000, .index = PLLVAL(0x70, 1, 3), }, /* FVco 960.000000 */
+ { .frequency = 150000000, .index = PLLVAL(0x75, 3, 2), }, /* FVco 600.000000 */
+ { .frequency = 160000000, .index = PLLVAL(0x98, 4, 2), }, /* FVco 640.000000 */
+ { .frequency = 170000000, .index = PLLVAL(0x4d, 1, 2), }, /* FVco 680.000000 */
+ { .frequency = 180000000, .index = PLLVAL(0x70, 2, 2), }, /* FVco 720.000000 */
+ { .frequency = 190000000, .index = PLLVAL(0x57, 1, 2), }, /* FVco 760.000000 */
+ { .frequency = 200000000, .index = PLLVAL(0x5c, 1, 2), }, /* FVco 800.000000 */
+ { .frequency = 210000000, .index = PLLVAL(0x84, 2, 2), }, /* FVco 840.000000 */
+ { .frequency = 220000000, .index = PLLVAL(0x66, 1, 2), }, /* FVco 880.000000 */
+ { .frequency = 230000000, .index = PLLVAL(0x6b, 1, 2), }, /* FVco 920.000000 */
+ { .frequency = 240000000, .index = PLLVAL(0x70, 1, 2), }, /* FVco 960.000000 */
+ { .frequency = 300000000, .index = PLLVAL(0x75, 3, 1), }, /* FVco 600.000000 */
+ { .frequency = 310000000, .index = PLLVAL(0x93, 4, 1), }, /* FVco 620.000000 */
+ { .frequency = 320000000, .index = PLLVAL(0x98, 4, 1), }, /* FVco 640.000000 */
+ { .frequency = 330000000, .index = PLLVAL(0x66, 2, 1), }, /* FVco 660.000000 */
+ { .frequency = 340000000, .index = PLLVAL(0x4d, 1, 1), }, /* FVco 680.000000 */
+ { .frequency = 350000000, .index = PLLVAL(0xa7, 4, 1), }, /* FVco 700.000000 */
+ { .frequency = 360000000, .index = PLLVAL(0x70, 2, 1), }, /* FVco 720.000000 */
+ { .frequency = 370000000, .index = PLLVAL(0xb1, 4, 1), }, /* FVco 740.000000 */
+ { .frequency = 380000000, .index = PLLVAL(0x57, 1, 1), }, /* FVco 760.000000 */
+ { .frequency = 390000000, .index = PLLVAL(0x7a, 2, 1), }, /* FVco 780.000000 */
+ { .frequency = 400000000, .index = PLLVAL(0x5c, 1, 1), }, /* FVco 800.000000 */
+};
+
+static int s3c2440_plls12_add(struct sys_device *dev)
+{
+ struct clk *xtal_clk;
+ unsigned long xtal;
+
+ xtal_clk = clk_get(NULL, "xtal");
+ if (IS_ERR(xtal_clk))
+ return PTR_ERR(xtal_clk);
+
+ xtal = clk_get_rate(xtal_clk);
+ clk_put(xtal_clk);
+
+ if (xtal == 12000000) {
+ printk(KERN_INFO "Using PLL table for 12MHz crystal\n");
+ return s3c_plltab_register(s3c2440_plls_12,
+ ARRAY_SIZE(s3c2440_plls_12));
+ }
+
+ return 0;
+}
+
+static struct sysdev_driver s3c2440_plls12_drv = {
+ .add = s3c2440_plls12_add,
+};
+
+static int __init s3c2440_pll_12mhz(void)
+{
+ return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_plls12_drv);
+
+}
+
+arch_initcall(s3c2440_pll_12mhz);
+
+static struct sysdev_driver s3c2442_plls12_drv = {
+ .add = s3c2440_plls12_add,
+};
+
+static int __init s3c2442_pll_12mhz(void)
+{
+ return sysdev_driver_register(&s3c2442_sysclass, &s3c2442_plls12_drv);
+
+}
+
+arch_initcall(s3c2442_pll_12mhz);
diff --git a/arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c b/arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c
new file mode 100644
index 00000000000..7679af13a94
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c
@@ -0,0 +1,127 @@
+/* arch/arm/plat-s3c24xx/s3c2440-pll-16934400.c
+ *
+ * Copyright (c) 2006-2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ * Vincent Sanders <vince@arm.linux.org.uk>
+ *
+ * S3C2440/S3C2442 CPU PLL tables (16.93444MHz Crystal)
+ *
+ * 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/types.h>
+#include <linux/kernel.h>
+#include <linux/sysdev.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <plat/cpu.h>
+#include <plat/cpu-freq-core.h>
+
+static struct cpufreq_frequency_table s3c2440_plls_169344[] __initdata = {
+ { .frequency = 78019200, .index = PLLVAL(121, 5, 3), }, /* FVco 624.153600 */
+ { .frequency = 84067200, .index = PLLVAL(131, 5, 3), }, /* FVco 672.537600 */
+ { .frequency = 90115200, .index = PLLVAL(141, 5, 3), }, /* FVco 720.921600 */
+ { .frequency = 96163200, .index = PLLVAL(151, 5, 3), }, /* FVco 769.305600 */
+ { .frequency = 102135600, .index = PLLVAL(185, 6, 3), }, /* FVco 817.084800 */
+ { .frequency = 108259200, .index = PLLVAL(171, 5, 3), }, /* FVco 866.073600 */
+ { .frequency = 114307200, .index = PLLVAL(127, 3, 3), }, /* FVco 914.457600 */
+ { .frequency = 120234240, .index = PLLVAL(134, 3, 3), }, /* FVco 961.873920 */
+ { .frequency = 126161280, .index = PLLVAL(141, 3, 3), }, /* FVco 1009.290240 */
+ { .frequency = 132088320, .index = PLLVAL(148, 3, 3), }, /* FVco 1056.706560 */
+ { .frequency = 138015360, .index = PLLVAL(155, 3, 3), }, /* FVco 1104.122880 */
+ { .frequency = 144789120, .index = PLLVAL(163, 3, 3), }, /* FVco 1158.312960 */
+ { .frequency = 150100363, .index = PLLVAL(187, 9, 2), }, /* FVco 600.401454 */
+ { .frequency = 156038400, .index = PLLVAL(121, 5, 2), }, /* FVco 624.153600 */
+ { .frequency = 162086400, .index = PLLVAL(126, 5, 2), }, /* FVco 648.345600 */
+ { .frequency = 168134400, .index = PLLVAL(131, 5, 2), }, /* FVco 672.537600 */
+ { .frequency = 174048000, .index = PLLVAL(177, 7, 2), }, /* FVco 696.192000 */
+ { .frequency = 180230400, .index = PLLVAL(141, 5, 2), }, /* FVco 720.921600 */
+ { .frequency = 186278400, .index = PLLVAL(124, 4, 2), }, /* FVco 745.113600 */
+ { .frequency = 192326400, .index = PLLVAL(151, 5, 2), }, /* FVco 769.305600 */
+ { .frequency = 198132480, .index = PLLVAL(109, 3, 2), }, /* FVco 792.529920 */
+ { .frequency = 204271200, .index = PLLVAL(185, 6, 2), }, /* FVco 817.084800 */
+ { .frequency = 210268800, .index = PLLVAL(141, 4, 2), }, /* FVco 841.075200 */
+ { .frequency = 216518400, .index = PLLVAL(171, 5, 2), }, /* FVco 866.073600 */
+ { .frequency = 222264000, .index = PLLVAL(97, 2, 2), }, /* FVco 889.056000 */
+ { .frequency = 228614400, .index = PLLVAL(127, 3, 2), }, /* FVco 914.457600 */
+ { .frequency = 234259200, .index = PLLVAL(158, 4, 2), }, /* FVco 937.036800 */
+ { .frequency = 240468480, .index = PLLVAL(134, 3, 2), }, /* FVco 961.873920 */
+ { .frequency = 246960000, .index = PLLVAL(167, 4, 2), }, /* FVco 987.840000 */
+ { .frequency = 252322560, .index = PLLVAL(141, 3, 2), }, /* FVco 1009.290240 */
+ { .frequency = 258249600, .index = PLLVAL(114, 2, 2), }, /* FVco 1032.998400 */
+ { .frequency = 264176640, .index = PLLVAL(148, 3, 2), }, /* FVco 1056.706560 */
+ { .frequency = 270950400, .index = PLLVAL(120, 2, 2), }, /* FVco 1083.801600 */
+ { .frequency = 276030720, .index = PLLVAL(155, 3, 2), }, /* FVco 1104.122880 */
+ { .frequency = 282240000, .index = PLLVAL(92, 1, 2), }, /* FVco 1128.960000 */
+ { .frequency = 289578240, .index = PLLVAL(163, 3, 2), }, /* FVco 1158.312960 */
+ { .frequency = 294235200, .index = PLLVAL(131, 2, 2), }, /* FVco 1176.940800 */
+ { .frequency = 300200727, .index = PLLVAL(187, 9, 1), }, /* FVco 600.401454 */
+ { .frequency = 306358690, .index = PLLVAL(191, 9, 1), }, /* FVco 612.717380 */
+ { .frequency = 312076800, .index = PLLVAL(121, 5, 1), }, /* FVco 624.153600 */
+ { .frequency = 318366720, .index = PLLVAL(86, 3, 1), }, /* FVco 636.733440 */
+ { .frequency = 324172800, .index = PLLVAL(126, 5, 1), }, /* FVco 648.345600 */
+ { .frequency = 330220800, .index = PLLVAL(109, 4, 1), }, /* FVco 660.441600 */
+ { .frequency = 336268800, .index = PLLVAL(131, 5, 1), }, /* FVco 672.537600 */
+ { .frequency = 342074880, .index = PLLVAL(93, 3, 1), }, /* FVco 684.149760 */
+ { .frequency = 348096000, .index = PLLVAL(177, 7, 1), }, /* FVco 696.192000 */
+ { .frequency = 355622400, .index = PLLVAL(118, 4, 1), }, /* FVco 711.244800 */
+ { .frequency = 360460800, .index = PLLVAL(141, 5, 1), }, /* FVco 720.921600 */
+ { .frequency = 366206400, .index = PLLVAL(165, 6, 1), }, /* FVco 732.412800 */
+ { .frequency = 372556800, .index = PLLVAL(124, 4, 1), }, /* FVco 745.113600 */
+ { .frequency = 378201600, .index = PLLVAL(126, 4, 1), }, /* FVco 756.403200 */
+ { .frequency = 384652800, .index = PLLVAL(151, 5, 1), }, /* FVco 769.305600 */
+ { .frequency = 391608000, .index = PLLVAL(177, 6, 1), }, /* FVco 783.216000 */
+ { .frequency = 396264960, .index = PLLVAL(109, 3, 1), }, /* FVco 792.529920 */
+ { .frequency = 402192000, .index = PLLVAL(87, 2, 1), }, /* FVco 804.384000 */
+};
+
+static int s3c2440_plls169344_add(struct sys_device *dev)
+{
+ struct clk *xtal_clk;
+ unsigned long xtal;
+
+ xtal_clk = clk_get(NULL, "xtal");
+ if (IS_ERR(xtal_clk))
+ return PTR_ERR(xtal_clk);
+
+ xtal = clk_get_rate(xtal_clk);
+ clk_put(xtal_clk);
+
+ if (xtal == 169344000) {
+ printk(KERN_INFO "Using PLL table for 16.9344MHz crystal\n");
+ return s3c_plltab_register(s3c2440_plls_169344,
+ ARRAY_SIZE(s3c2440_plls_169344));
+ }
+
+ return 0;
+}
+
+static struct sysdev_driver s3c2440_plls169344_drv = {
+ .add = s3c2440_plls169344_add,
+};
+
+static int __init s3c2440_pll_16934400(void)
+{
+ return sysdev_driver_register(&s3c2440_sysclass,
+ &s3c2440_plls169344_drv);
+
+}
+
+arch_initcall(s3c2440_pll_16934400);
+
+static struct sysdev_driver s3c2442_plls169344_drv = {
+ .add = s3c2440_plls169344_add,
+};
+
+static int __init s3c2442_pll_16934400(void)
+{
+ return sysdev_driver_register(&s3c2442_sysclass,
+ &s3c2442_plls169344_drv);
+
+}
+
+arch_initcall(s3c2442_pll_16934400);
diff --git a/arch/arm/plat-s3c24xx/spi-bus1-gpd8_9_10.c b/arch/arm/plat-s3c24xx/spi-bus1-gpd8_9_10.c
new file mode 100644
index 00000000000..89fcf5308cf
--- /dev/null
+++ b/arch/arm/plat-s3c24xx/spi-bus1-gpd8_9_10.c
@@ -0,0 +1,38 @@
+/* linux/arch/arm/plat-s3c24xx/spi-bus0-gpd8_9_10.c
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX SPI - gpio configuration for bus 1 on gpd8,9,10
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+*/
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+
+#include <mach/spi.h>
+#include <mach/regs-gpio.h>
+
+void s3c24xx_spi_gpiocfg_bus1_gpd8_9_10(struct s3c2410_spi_info *spi,
+ int enable)
+{
+
+ printk(KERN_INFO "%s(%d)\n", __func__, enable);
+ if (enable) {
+ s3c2410_gpio_cfgpin(S3C2410_GPD(10), S3C2440_GPD10_SPICLK1);
+ s3c2410_gpio_cfgpin(S3C2410_GPD(9), S3C2440_GPD9_SPIMOSI1);
+ s3c2410_gpio_cfgpin(S3C2410_GPD(8), S3C2440_GPD8_SPIMISO1);
+ s3c2410_gpio_pullup(S3C2410_GPD(10), 0);
+ s3c2410_gpio_pullup(S3C2410_GPD(9), 0);
+ } else {
+ s3c2410_gpio_cfgpin(S3C2410_GPD(8), S3C2410_GPIO_INPUT);
+ s3c2410_gpio_cfgpin(S3C2410_GPD(9), S3C2410_GPIO_INPUT);
+ s3c2410_gpio_pullup(S3C2410_GPD(10), 1);
+ s3c2410_gpio_pullup(S3C2410_GPD(9), 1);
+ s3c2410_gpio_pullup(S3C2410_GPD(8), 1);
+ }
+}
diff --git a/arch/arm/plat-s3c64xx/Kconfig b/arch/arm/plat-s3c64xx/Kconfig
index 5ebd8b425a5..bcfa778614d 100644
--- a/arch/arm/plat-s3c64xx/Kconfig
+++ b/arch/arm/plat-s3c64xx/Kconfig
@@ -19,6 +19,7 @@ config PLAT_S3C64XX
select S3C_GPIO_PULL_UPDOWN
select S3C_GPIO_CFG_S3C24XX
select S3C_GPIO_CFG_S3C64XX
+ select S3C_DEV_NAND
select USB_ARCH_HAS_OHCI
help
Base platform code for any Samsung S3C64XX device
diff --git a/arch/arm/plat-s3c64xx/Makefile b/arch/arm/plat-s3c64xx/Makefile
index 3c8882cd626..b85b4359e93 100644
--- a/arch/arm/plat-s3c64xx/Makefile
+++ b/arch/arm/plat-s3c64xx/Makefile
@@ -40,4 +40,5 @@ obj-$(CONFIG_S3C64XX_DMA) += dma.o
obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o
obj-$(CONFIG_S3C64XX_SETUP_I2C1) += setup-i2c1.o
obj-$(CONFIG_S3C64XX_SETUP_FB_24BPP) += setup-fb-24bpp.o
-obj-$(CONFIG_S3C64XX_SETUP_SDHCI_GPIO) += setup-sdhci-gpio.o \ No newline at end of file
+obj-$(CONFIG_S3C64XX_SETUP_SDHCI_GPIO) += setup-sdhci-gpio.o
+obj-$(CONFIG_SND_S3C24XX_SOC) += dev-audio.o
diff --git a/arch/arm/plat-s3c/dev-audio.c b/arch/arm/plat-s3c64xx/dev-audio.c
index 1322beb40dd..1322beb40dd 100644
--- a/arch/arm/plat-s3c/dev-audio.c
+++ b/arch/arm/plat-s3c64xx/dev-audio.c
diff --git a/arch/arm/plat-s5pc1xx/Kconfig b/arch/arm/plat-s5pc1xx/Kconfig
new file mode 100644
index 00000000000..a8a711c3c06
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/Kconfig
@@ -0,0 +1,50 @@
+# arch/arm/plat-s5pc1xx/Kconfig
+#
+# Copyright 2009 Samsung Electronics Co.
+# Byungho Min <bhmin@samsung.com>
+#
+# Licensed under GPLv2
+
+config PLAT_S5PC1XX
+ bool
+ depends on ARCH_S5PC1XX
+ default y
+ select PLAT_S3C
+ select ARM_VIC
+ select NO_IOPORT
+ select ARCH_REQUIRE_GPIOLIB
+ select S3C_GPIO_TRACK
+ select S3C_GPIO_PULL_UPDOWN
+ help
+ Base platform code for any Samsung S5PC1XX device
+
+if PLAT_S5PC1XX
+
+# Configuration options shared by all S3C64XX implementations
+
+config CPU_S5PC100_INIT
+ bool
+ help
+ Common initialisation code for the S5PC1XX
+
+config CPU_S5PC100_CLOCK
+ bool
+ help
+ Common clock support code for the S5PC1XX
+
+# platform specific device setup
+
+config S5PC100_SETUP_I2C0
+ bool
+ default y
+ help
+ Common setup code for i2c bus 0.
+
+ Note, currently since i2c0 is always compiled, this setup helper
+ is always compiled with it.
+
+config S5PC100_SETUP_I2C1
+ bool
+ help
+ Common setup code for i2c bus 1.
+endif
diff --git a/arch/arm/plat-s5pc1xx/Makefile b/arch/arm/plat-s5pc1xx/Makefile
new file mode 100644
index 00000000000..f1ecb2c37ee
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/Makefile
@@ -0,0 +1,26 @@
+# arch/arm/plat-s5pc1xx/Makefile
+#
+# Copyright 2009 Samsung Electronics Co.
+#
+# Licensed under GPLv2
+
+obj-y :=
+obj-m :=
+obj-n := dummy.o
+obj- :=
+
+# Core files
+
+obj-y += dev-uart.o
+obj-y += cpu.o
+obj-y += irq.o
+
+# CPU support
+
+obj-$(CONFIG_CPU_S5PC100_INIT) += s5pc100-init.o
+obj-$(CONFIG_CPU_S5PC100_CLOCK) += s5pc100-clock.o
+
+# Device setup
+
+obj-$(CONFIG_S5PC100_SETUP_I2C0) += setup-i2c0.o
+obj-$(CONFIG_S5PC100_SETUP_I2C1) += setup-i2c1.o
diff --git a/arch/arm/plat-s5pc1xx/cpu.c b/arch/arm/plat-s5pc1xx/cpu.c
new file mode 100644
index 00000000000..715a7330794
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/cpu.c
@@ -0,0 +1,112 @@
+/* linux/arch/arm/plat-s5pc1xx/cpu.c
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC1XX CPU Support
+ *
+ * Based on plat-s3c64xx/cpu.c
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/serial_core.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/map.h>
+
+#include <asm/mach/map.h>
+
+#include <plat/regs-serial.h>
+
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/clock.h>
+
+#include <plat/s5pc100.h>
+
+/* table of supported CPUs */
+
+static const char name_s5pc100[] = "S5PC100";
+
+static struct cpu_table cpu_ids[] __initdata = {
+ {
+ .idcode = 0x43100000,
+ .idmask = 0xfffff000,
+ .map_io = s5pc100_map_io,
+ .init_clocks = s5pc100_init_clocks,
+ .init_uarts = s5pc100_init_uarts,
+ .init = s5pc100_init,
+ .name = name_s5pc100,
+ },
+};
+/* minimal IO mapping */
+
+/* see notes on uart map in arch/arm/mach-s5pc100/include/mach/debug-macro.S */
+#define UART_OFFS (S3C_PA_UART & 0xffff)
+
+static struct map_desc s5pc1xx_iodesc[] __initdata = {
+ {
+ .virtual = (unsigned long)S5PC1XX_VA_CHIPID,
+ .pfn = __phys_to_pfn(S5PC1XX_PA_CHIPID),
+ .length = SZ_16,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S5PC1XX_VA_CLK,
+ .pfn = __phys_to_pfn(S5PC1XX_PA_CLK),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S5PC1XX_VA_PWR,
+ .pfn = __phys_to_pfn(S5PC1XX_PA_PWR),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)(S5PC1XX_VA_UART),
+ .pfn = __phys_to_pfn(S5PC1XX_PA_UART),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S5PC1XX_VA_VIC(0),
+ .pfn = __phys_to_pfn(S5PC1XX_PA_VIC(0)),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S5PC1XX_VA_VIC(1),
+ .pfn = __phys_to_pfn(S5PC1XX_PA_VIC(1)),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S5PC1XX_VA_VIC(2),
+ .pfn = __phys_to_pfn(S5PC1XX_PA_VIC(2)),
+ .length = SZ_4K,
+ .type = MT_DEVICE,
+ }, {
+ .virtual = (unsigned long)S5PC1XX_VA_TIMER,
+ .pfn = __phys_to_pfn(S5PC1XX_PA_TIMER),
+ .length = SZ_256,
+ .type = MT_DEVICE,
+ },
+};
+
+/* read cpu identification code */
+
+void __init s5pc1xx_init_io(struct map_desc *mach_desc, int size)
+{
+ unsigned long idcode;
+
+ /* initialise the io descriptors we need for initialisation */
+ iotable_init(s5pc1xx_iodesc, ARRAY_SIZE(s5pc1xx_iodesc));
+ iotable_init(mach_desc, size);
+
+ idcode = __raw_readl(S5PC1XX_VA_CHIPID);
+ s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids));
+}
diff --git a/arch/arm/plat-s5pc1xx/dev-uart.c b/arch/arm/plat-s5pc1xx/dev-uart.c
new file mode 100644
index 00000000000..f749bc5407b
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/dev-uart.c
@@ -0,0 +1,174 @@
+/* linux/arch/arm/plat-s5pc1xx/dev-uart.c
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * Based on plat-s3c64xx/dev-uart.c
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/irq.h>
+#include <mach/hardware.h>
+#include <mach/map.h>
+
+#include <plat/devs.h>
+
+/* Serial port registrations */
+
+/* 64xx uarts are closer together */
+
+static struct resource s5pc1xx_uart0_resource[] = {
+ [0] = {
+ .start = S3C_PA_UART0,
+ .end = S3C_PA_UART0 + 0x100,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3CUART_RX0,
+ .end = IRQ_S3CUART_RX0,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_S3CUART_TX0,
+ .end = IRQ_S3CUART_TX0,
+ .flags = IORESOURCE_IRQ,
+
+ },
+ [3] = {
+ .start = IRQ_S3CUART_ERR0,
+ .end = IRQ_S3CUART_ERR0,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct resource s5pc1xx_uart1_resource[] = {
+ [0] = {
+ .start = S3C_PA_UART1,
+ .end = S3C_PA_UART1 + 0x100,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3CUART_RX1,
+ .end = IRQ_S3CUART_RX1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_S3CUART_TX1,
+ .end = IRQ_S3CUART_TX1,
+ .flags = IORESOURCE_IRQ,
+
+ },
+ [3] = {
+ .start = IRQ_S3CUART_ERR1,
+ .end = IRQ_S3CUART_ERR1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource s5pc1xx_uart2_resource[] = {
+ [0] = {
+ .start = S3C_PA_UART2,
+ .end = S3C_PA_UART2 + 0x100,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3CUART_RX2,
+ .end = IRQ_S3CUART_RX2,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_S3CUART_TX2,
+ .end = IRQ_S3CUART_TX2,
+ .flags = IORESOURCE_IRQ,
+
+ },
+ [3] = {
+ .start = IRQ_S3CUART_ERR2,
+ .end = IRQ_S3CUART_ERR2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct resource s5pc1xx_uart3_resource[] = {
+ [0] = {
+ .start = S3C_PA_UART3,
+ .end = S3C_PA_UART3 + 0x100,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_S3CUART_RX3,
+ .end = IRQ_S3CUART_RX3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .start = IRQ_S3CUART_TX3,
+ .end = IRQ_S3CUART_TX3,
+ .flags = IORESOURCE_IRQ,
+
+ },
+ [3] = {
+ .start = IRQ_S3CUART_ERR3,
+ .end = IRQ_S3CUART_ERR3,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+
+struct s3c24xx_uart_resources s5pc1xx_uart_resources[] __initdata = {
+ [0] = {
+ .resources = s5pc1xx_uart0_resource,
+ .nr_resources = ARRAY_SIZE(s5pc1xx_uart0_resource),
+ },
+ [1] = {
+ .resources = s5pc1xx_uart1_resource,
+ .nr_resources = ARRAY_SIZE(s5pc1xx_uart1_resource),
+ },
+ [2] = {
+ .resources = s5pc1xx_uart2_resource,
+ .nr_resources = ARRAY_SIZE(s5pc1xx_uart2_resource),
+ },
+ [3] = {
+ .resources = s5pc1xx_uart3_resource,
+ .nr_resources = ARRAY_SIZE(s5pc1xx_uart3_resource),
+ },
+};
+
+/* uart devices */
+
+static struct platform_device s3c24xx_uart_device0 = {
+ .id = 0,
+};
+
+static struct platform_device s3c24xx_uart_device1 = {
+ .id = 1,
+};
+
+static struct platform_device s3c24xx_uart_device2 = {
+ .id = 2,
+};
+
+static struct platform_device s3c24xx_uart_device3 = {
+ .id = 3,
+};
+
+struct platform_device *s3c24xx_uart_src[4] = {
+ &s3c24xx_uart_device0,
+ &s3c24xx_uart_device1,
+ &s3c24xx_uart_device2,
+ &s3c24xx_uart_device3,
+};
+
+struct platform_device *s3c24xx_uart_devs[4] = {
+};
+
diff --git a/arch/arm/plat-s5pc1xx/include/plat/irqs.h b/arch/arm/plat-s5pc1xx/include/plat/irqs.h
new file mode 100644
index 00000000000..f07d8c3b25d
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/include/plat/irqs.h
@@ -0,0 +1,182 @@
+/* linux/arch/arm/plat-s5pc1xx/include/plat/irqs.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC1XX - Common IRQ support
+ *
+ * Based on plat-s3c64xx/include/plat/irqs.h
+ */
+
+#ifndef __ASM_PLAT_S5PC1XX_IRQS_H
+#define __ASM_PLAT_S5PC1XX_IRQS_H __FILE__
+
+/* we keep the first set of CPU IRQs out of the range of
+ * the ISA space, so that the PC104 has them to itself
+ * and we don't end up having to do horrible things to the
+ * standard ISA drivers....
+ *
+ * note, since we're using the VICs, our start must be a
+ * mulitple of 32 to allow the common code to work
+ */
+
+#define S3C_IRQ_OFFSET (32)
+
+#define S3C_IRQ(x) ((x) + S3C_IRQ_OFFSET)
+
+#define S3C_VIC0_BASE S3C_IRQ(0)
+#define S3C_VIC1_BASE S3C_IRQ(32)
+#define S3C_VIC2_BASE S3C_IRQ(64)
+
+/* UART interrupts, each UART has 4 intterupts per channel so
+ * use the space between the ISA and S3C main interrupts. Note, these
+ * are not in the same order as the S3C24XX series! */
+
+#define IRQ_S3CUART_BASE0 (16)
+#define IRQ_S3CUART_BASE1 (20)
+#define IRQ_S3CUART_BASE2 (24)
+#define IRQ_S3CUART_BASE3 (28)
+
+#define UART_IRQ_RXD (0)
+#define UART_IRQ_ERR (1)
+#define UART_IRQ_TXD (2)
+#define UART_IRQ_MODEM (3)
+
+#define IRQ_S3CUART_RX0 (IRQ_S3CUART_BASE0 + UART_IRQ_RXD)
+#define IRQ_S3CUART_TX0 (IRQ_S3CUART_BASE0 + UART_IRQ_TXD)
+#define IRQ_S3CUART_ERR0 (IRQ_S3CUART_BASE0 + UART_IRQ_ERR)
+
+#define IRQ_S3CUART_RX1 (IRQ_S3CUART_BASE1 + UART_IRQ_RXD)
+#define IRQ_S3CUART_TX1 (IRQ_S3CUART_BASE1 + UART_IRQ_TXD)
+#define IRQ_S3CUART_ERR1 (IRQ_S3CUART_BASE1 + UART_IRQ_ERR)
+
+#define IRQ_S3CUART_RX2 (IRQ_S3CUART_BASE2 + UART_IRQ_RXD)
+#define IRQ_S3CUART_TX2 (IRQ_S3CUART_BASE2 + UART_IRQ_TXD)
+#define IRQ_S3CUART_ERR2 (IRQ_S3CUART_BASE2 + UART_IRQ_ERR)
+
+#define IRQ_S3CUART_RX3 (IRQ_S3CUART_BASE3 + UART_IRQ_RXD)
+#define IRQ_S3CUART_TX3 (IRQ_S3CUART_BASE3 + UART_IRQ_TXD)
+#define IRQ_S3CUART_ERR3 (IRQ_S3CUART_BASE3 + UART_IRQ_ERR)
+
+/* VIC based IRQs */
+
+#define S5PC1XX_IRQ_VIC0(x) (S3C_VIC0_BASE + (x))
+#define S5PC1XX_IRQ_VIC1(x) (S3C_VIC1_BASE + (x))
+#define S5PC1XX_IRQ_VIC2(x) (S3C_VIC2_BASE + (x))
+
+/*
+ * VIC0: system, DMA, timer
+ */
+#define IRQ_EINT0 S5PC1XX_IRQ_VIC0(0)
+#define IRQ_EINT1 S5PC1XX_IRQ_VIC0(1)
+#define IRQ_EINT2 S5PC1XX_IRQ_VIC0(2)
+#define IRQ_EINT3 S5PC1XX_IRQ_VIC0(3)
+#define IRQ_EINT4 S5PC1XX_IRQ_VIC0(4)
+#define IRQ_EINT5 S5PC1XX_IRQ_VIC0(5)
+#define IRQ_EINT6 S5PC1XX_IRQ_VIC0(6)
+#define IRQ_EINT7 S5PC1XX_IRQ_VIC0(7)
+#define IRQ_EINT8 S5PC1XX_IRQ_VIC0(8)
+#define IRQ_EINT9 S5PC1XX_IRQ_VIC0(9)
+#define IRQ_EINT10 S5PC1XX_IRQ_VIC0(10)
+#define IRQ_EINT11 S5PC1XX_IRQ_VIC0(11)
+#define IRQ_EINT12 S5PC1XX_IRQ_VIC0(12)
+#define IRQ_EINT13 S5PC1XX_IRQ_VIC0(13)
+#define IRQ_EINT14 S5PC1XX_IRQ_VIC0(14)
+#define IRQ_EINT15 S5PC1XX_IRQ_VIC0(15)
+#define IRQ_EINT16_31 S5PC1XX_IRQ_VIC0(16)
+#define IRQ_BATF S5PC1XX_IRQ_VIC0(17)
+#define IRQ_MDMA S5PC1XX_IRQ_VIC0(18)
+#define IRQ_PDMA0 S5PC1XX_IRQ_VIC0(19)
+#define IRQ_PDMA1 S5PC1XX_IRQ_VIC0(20)
+#define IRQ_TIMER0 S5PC1XX_IRQ_VIC0(21)
+#define IRQ_TIMER1 S5PC1XX_IRQ_VIC0(22)
+#define IRQ_TIMER2 S5PC1XX_IRQ_VIC0(23)
+#define IRQ_TIMER3 S5PC1XX_IRQ_VIC0(24)
+#define IRQ_TIMER4 S5PC1XX_IRQ_VIC0(25)
+#define IRQ_SYSTIMER S5PC1XX_IRQ_VIC0(26)
+#define IRQ_WDT S5PC1XX_IRQ_VIC0(27)
+#define IRQ_RTC_ALARM S5PC1XX_IRQ_VIC0(28)
+#define IRQ_RTC_TIC S5PC1XX_IRQ_VIC0(29)
+#define IRQ_GPIOINT S5PC1XX_IRQ_VIC0(30)
+
+/*
+ * VIC1: ARM, power, memory, connectivity
+ */
+#define IRQ_CORTEX0 S5PC1XX_IRQ_VIC1(0)
+#define IRQ_CORTEX1 S5PC1XX_IRQ_VIC1(1)
+#define IRQ_CORTEX2 S5PC1XX_IRQ_VIC1(2)
+#define IRQ_CORTEX3 S5PC1XX_IRQ_VIC1(3)
+#define IRQ_CORTEX4 S5PC1XX_IRQ_VIC1(4)
+#define IRQ_IEMAPC S5PC1XX_IRQ_VIC1(5)
+#define IRQ_IEMIEC S5PC1XX_IRQ_VIC1(6)
+#define IRQ_ONENAND S5PC1XX_IRQ_VIC1(7)
+#define IRQ_NFC S5PC1XX_IRQ_VIC1(8)
+#define IRQ_CFC S5PC1XX_IRQ_VIC1(9)
+#define IRQ_UART0 S5PC1XX_IRQ_VIC1(10)
+#define IRQ_UART1 S5PC1XX_IRQ_VIC1(11)
+#define IRQ_UART2 S5PC1XX_IRQ_VIC1(12)
+#define IRQ_UART3 S5PC1XX_IRQ_VIC1(13)
+#define IRQ_IIC S5PC1XX_IRQ_VIC1(14)
+#define IRQ_SPI0 S5PC1XX_IRQ_VIC1(15)
+#define IRQ_SPI1 S5PC1XX_IRQ_VIC1(16)
+#define IRQ_SPI2 S5PC1XX_IRQ_VIC1(17)
+#define IRQ_IRDA S5PC1XX_IRQ_VIC1(18)
+#define IRQ_CAN0 S5PC1XX_IRQ_VIC1(19)
+#define IRQ_CAN1 S5PC1XX_IRQ_VIC1(20)
+#define IRQ_HSIRX S5PC1XX_IRQ_VIC1(21)
+#define IRQ_HSITX S5PC1XX_IRQ_VIC1(22)
+#define IRQ_UHOST S5PC1XX_IRQ_VIC1(23)
+#define IRQ_OTG S5PC1XX_IRQ_VIC1(24)
+#define IRQ_MSM S5PC1XX_IRQ_VIC1(25)
+#define IRQ_HSMMC0 S5PC1XX_IRQ_VIC1(26)
+#define IRQ_HSMMC1 S5PC1XX_IRQ_VIC1(27)
+#define IRQ_HSMMC2 S5PC1XX_IRQ_VIC1(28)
+#define IRQ_MIPICSI S5PC1XX_IRQ_VIC1(29)
+#define IRQ_MIPIDSI S5PC1XX_IRQ_VIC1(30)
+
+/*
+ * VIC2: multimedia, audio, security
+ */
+#define IRQ_LCD0 S5PC1XX_IRQ_VIC2(0)
+#define IRQ_LCD1 S5PC1XX_IRQ_VIC2(1)
+#define IRQ_LCD2 S5PC1XX_IRQ_VIC2(2)
+#define IRQ_LCD3 S5PC1XX_IRQ_VIC2(3)
+#define IRQ_ROTATOR S5PC1XX_IRQ_VIC2(4)
+#define IRQ_FIMC0 S5PC1XX_IRQ_VIC2(5)
+#define IRQ_FIMC1 S5PC1XX_IRQ_VIC2(6)
+#define IRQ_FIMC2 S5PC1XX_IRQ_VIC2(7)
+#define IRQ_JPEG S5PC1XX_IRQ_VIC2(8)
+#define IRQ_2D S5PC1XX_IRQ_VIC2(9)
+#define IRQ_3D S5PC1XX_IRQ_VIC2(10)
+#define IRQ_MIXER S5PC1XX_IRQ_VIC2(11)
+#define IRQ_HDMI S5PC1XX_IRQ_VIC2(12)
+#define IRQ_IIC1 S5PC1XX_IRQ_VIC2(13)
+#define IRQ_MFC S5PC1XX_IRQ_VIC2(14)
+#define IRQ_TVENC S5PC1XX_IRQ_VIC2(15)
+#define IRQ_I2S0 S5PC1XX_IRQ_VIC2(16)
+#define IRQ_I2S1 S5PC1XX_IRQ_VIC2(17)
+#define IRQ_I2S2 S5PC1XX_IRQ_VIC2(18)
+#define IRQ_AC97 S5PC1XX_IRQ_VIC2(19)
+#define IRQ_PCM0 S5PC1XX_IRQ_VIC2(20)
+#define IRQ_PCM1 S5PC1XX_IRQ_VIC2(21)
+#define IRQ_SPDIF S5PC1XX_IRQ_VIC2(22)
+#define IRQ_ADC S5PC1XX_IRQ_VIC2(23)
+#define IRQ_PENDN S5PC1XX_IRQ_VIC2(24)
+#define IRQ_TC IRQ_PENDN
+#define IRQ_KEYPAD S5PC1XX_IRQ_VIC2(25)
+#define IRQ_CG S5PC1XX_IRQ_VIC2(26)
+#define IRQ_SEC S5PC1XX_IRQ_VIC2(27)
+#define IRQ_SECRX S5PC1XX_IRQ_VIC2(28)
+#define IRQ_SECTX S5PC1XX_IRQ_VIC2(29)
+#define IRQ_SDMIRQ S5PC1XX_IRQ_VIC2(30)
+#define IRQ_SDMFIQ S5PC1XX_IRQ_VIC2(31)
+
+#define S3C_IRQ_EINT_BASE (IRQ_SDMFIQ + 1)
+
+#define S3C_EINT(x) ((x) + S3C_IRQ_EINT_BASE)
+#define IRQ_EINT(x) S3C_EINT(x)
+
+#define NR_IRQS (IRQ_EINT(31)+1)
+
+#endif /* __ASM_PLAT_S5PC1XX_IRQS_H */
+
diff --git a/arch/arm/plat-s5pc1xx/include/plat/pll.h b/arch/arm/plat-s5pc1xx/include/plat/pll.h
new file mode 100644
index 00000000000..21afef1573e
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/include/plat/pll.h
@@ -0,0 +1,38 @@
+/* arch/arm/plat-s5pc1xx/include/plat/pll.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC1XX PLL code
+ *
+ * Based on plat-s3c64xx/include/plat/pll.h
+ *
+ * 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.
+*/
+
+#define S5P_PLL_MDIV_MASK ((1 << (25-16+1)) - 1)
+#define S5P_PLL_PDIV_MASK ((1 << (13-8+1)) - 1)
+#define S5P_PLL_SDIV_MASK ((1 << (2-0+1)) - 1)
+#define S5P_PLL_MDIV_SHIFT (16)
+#define S5P_PLL_PDIV_SHIFT (8)
+#define S5P_PLL_SDIV_SHIFT (0)
+
+#include <asm/div64.h>
+
+static inline unsigned long s5pc1xx_get_pll(unsigned long baseclk,
+ u32 pllcon)
+{
+ u32 mdiv, pdiv, sdiv;
+ u64 fvco = baseclk;
+
+ mdiv = (pllcon >> S5P_PLL_MDIV_SHIFT) & S5P_PLL_MDIV_MASK;
+ pdiv = (pllcon >> S5P_PLL_PDIV_SHIFT) & S5P_PLL_PDIV_MASK;
+ sdiv = (pllcon >> S5P_PLL_SDIV_SHIFT) & S5P_PLL_SDIV_MASK;
+
+ fvco *= mdiv;
+ do_div(fvco, (pdiv << sdiv));
+
+ return (unsigned long)fvco;
+}
diff --git a/arch/arm/plat-s5pc1xx/include/plat/regs-clock.h b/arch/arm/plat-s5pc1xx/include/plat/regs-clock.h
new file mode 100644
index 00000000000..75c8390cb82
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/include/plat/regs-clock.h
@@ -0,0 +1,421 @@
+/* arch/arm/plat-s5pc1xx/include/plat/regs-clock.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC1XX clock register definitions
+ *
+ * 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 __PLAT_REGS_CLOCK_H
+#define __PLAT_REGS_CLOCK_H __FILE__
+
+#define S5PC1XX_CLKREG(x) (S5PC1XX_VA_CLK + (x))
+
+#define S5PC1XX_APLL_LOCK S5PC1XX_CLKREG(0x00)
+#define S5PC1XX_MPLL_LOCK S5PC1XX_CLKREG(0x04)
+#define S5PC1XX_EPLL_LOCK S5PC1XX_CLKREG(0x08)
+#define S5PC100_HPLL_LOCK S5PC1XX_CLKREG(0x0C)
+
+#define S5PC1XX_APLL_CON S5PC1XX_CLKREG(0x100)
+#define S5PC1XX_MPLL_CON S5PC1XX_CLKREG(0x104)
+#define S5PC1XX_EPLL_CON S5PC1XX_CLKREG(0x108)
+#define S5PC100_HPLL_CON S5PC1XX_CLKREG(0x10C)
+
+#define S5PC1XX_CLK_SRC0 S5PC1XX_CLKREG(0x200)
+#define S5PC1XX_CLK_SRC1 S5PC1XX_CLKREG(0x204)
+#define S5PC1XX_CLK_SRC2 S5PC1XX_CLKREG(0x208)
+#define S5PC1XX_CLK_SRC3 S5PC1XX_CLKREG(0x20C)
+
+#define S5PC1XX_CLK_DIV0 S5PC1XX_CLKREG(0x300)
+#define S5PC1XX_CLK_DIV1 S5PC1XX_CLKREG(0x304)
+#define S5PC1XX_CLK_DIV2 S5PC1XX_CLKREG(0x308)
+#define S5PC1XX_CLK_DIV3 S5PC1XX_CLKREG(0x30C)
+#define S5PC1XX_CLK_DIV4 S5PC1XX_CLKREG(0x310)
+
+#define S5PC100_CLK_OUT S5PC1XX_CLKREG(0x400)
+
+#define S5PC100_CLKGATE_D00 S5PC1XX_CLKREG(0x500)
+#define S5PC100_CLKGATE_D01 S5PC1XX_CLKREG(0x504)
+#define S5PC100_CLKGATE_D02 S5PC1XX_CLKREG(0x508)
+
+#define S5PC100_CLKGATE_D10 S5PC1XX_CLKREG(0x520)
+#define S5PC100_CLKGATE_D11 S5PC1XX_CLKREG(0x524)
+#define S5PC100_CLKGATE_D12 S5PC1XX_CLKREG(0x528)
+#define S5PC100_CLKGATE_D13 S5PC1XX_CLKREG(0x52C)
+#define S5PC100_CLKGATE_D14 S5PC1XX_CLKREG(0x530)
+#define S5PC100_CLKGATE_D15 S5PC1XX_CLKREG(0x534)
+
+#define S5PC100_CLKGATE_D20 S5PC1XX_CLKREG(0x540)
+
+#define S5PC100_SCLKGATE0 S5PC1XX_CLKREG(0x560)
+#define S5PC100_SCLKGATE1 S5PC1XX_CLKREG(0x564)
+
+#define S5PC100_OTHERS S5PC1XX_CLKREG(0x8200)
+
+#define S5PC1XX_EPLL_EN (1<<31)
+#define S5PC1XX_EPLL_MASK 0xffffffff
+#define S5PC1XX_EPLLVAL(_m, _p, _s) ((_m) << 16 | ((_p) << 8) | ((_s)))
+
+/* CLKSRC0 */
+#define S5PC1XX_CLKSRC0_APLL_MASK (0x1<<0)
+#define S5PC1XX_CLKSRC0_APLL_SHIFT (0)
+#define S5PC1XX_CLKSRC0_MPLL_MASK (0x1<<4)
+#define S5PC1XX_CLKSRC0_MPLL_SHIFT (4)
+#define S5PC1XX_CLKSRC0_EPLL_MASK (0x1<<8)
+#define S5PC1XX_CLKSRC0_EPLL_SHIFT (8)
+#define S5PC100_CLKSRC0_HPLL_MASK (0x1<<12)
+#define S5PC100_CLKSRC0_HPLL_SHIFT (12)
+#define S5PC100_CLKSRC0_AMMUX_MASK (0x1<<16)
+#define S5PC100_CLKSRC0_AMMUX_SHIFT (16)
+#define S5PC100_CLKSRC0_HREF_MASK (0x1<<20)
+#define S5PC100_CLKSRC0_HREF_SHIFT (20)
+#define S5PC1XX_CLKSRC0_ONENAND_MASK (0x1<<24)
+#define S5PC1XX_CLKSRC0_ONENAND_SHIFT (24)
+
+
+/* CLKSRC1 */
+#define S5PC100_CLKSRC1_UART_MASK (0x1<<0)
+#define S5PC100_CLKSRC1_UART_SHIFT (0)
+#define S5PC100_CLKSRC1_SPI0_MASK (0x3<<4)
+#define S5PC100_CLKSRC1_SPI0_SHIFT (4)
+#define S5PC100_CLKSRC1_SPI1_MASK (0x3<<8)
+#define S5PC100_CLKSRC1_SPI1_SHIFT (8)
+#define S5PC100_CLKSRC1_SPI2_MASK (0x3<<12)
+#define S5PC100_CLKSRC1_SPI2_SHIFT (12)
+#define S5PC100_CLKSRC1_IRDA_MASK (0x3<<16)
+#define S5PC100_CLKSRC1_IRDA_SHIFT (16)
+#define S5PC100_CLKSRC1_UHOST_MASK (0x3<<20)
+#define S5PC100_CLKSRC1_UHOST_SHIFT (20)
+#define S5PC100_CLKSRC1_CLK48M_MASK (0x1<<24)
+#define S5PC100_CLKSRC1_CLK48M_SHIFT (24)
+
+/* CLKSRC2 */
+#define S5PC100_CLKSRC2_MMC0_MASK (0x3<<0)
+#define S5PC100_CLKSRC2_MMC0_SHIFT (0)
+#define S5PC100_CLKSRC2_MMC1_MASK (0x3<<4)
+#define S5PC100_CLKSRC2_MMC1_SHIFT (4)
+#define S5PC100_CLKSRC2_MMC2_MASK (0x3<<8)
+#define S5PC100_CLKSRC2_MMC2_SHIFT (8)
+#define S5PC100_CLKSRC2_LCD_MASK (0x3<<12)
+#define S5PC100_CLKSRC2_LCD_SHIFT (12)
+#define S5PC100_CLKSRC2_FIMC0_MASK (0x3<<16)
+#define S5PC100_CLKSRC2_FIMC0_SHIFT (16)
+#define S5PC100_CLKSRC2_FIMC1_MASK (0x3<<20)
+#define S5PC100_CLKSRC2_FIMC1_SHIFT (20)
+#define S5PC100_CLKSRC2_FIMC2_MASK (0x3<<24)
+#define S5PC100_CLKSRC2_FIMC2_SHIFT (24)
+#define S5PC100_CLKSRC2_MIXER_MASK (0x3<<28)
+#define S5PC100_CLKSRC2_MIXER_SHIFT (28)
+
+/* CLKSRC3 */
+#define S5PC100_CLKSRC3_PWI_MASK (0x3<<0)
+#define S5PC100_CLKSRC3_PWI_SHIFT (0)
+#define S5PC100_CLKSRC3_HCLKD2_MASK (0x1<<4)
+#define S5PC100_CLKSRC3_HCLKD2_SHIFT (4)
+#define S5PC100_CLKSRC3_I2SD2_MASK (0x3<<8)
+#define S5PC100_CLKSRC3_I2SD2_SHIFT (8)
+#define S5PC100_CLKSRC3_AUDIO0_MASK (0x7<<12)
+#define S5PC100_CLKSRC3_AUDIO0_SHIFT (12)
+#define S5PC100_CLKSRC3_AUDIO1_MASK (0x7<<16)
+#define S5PC100_CLKSRC3_AUDIO1_SHIFT (16)
+#define S5PC100_CLKSRC3_AUDIO2_MASK (0x7<<20)
+#define S5PC100_CLKSRC3_AUDIO2_SHIFT (20)
+#define S5PC100_CLKSRC3_SPDIF_MASK (0x3<<24)
+#define S5PC100_CLKSRC3_SPDIF_SHIFT (24)
+
+
+/* CLKDIV0 */
+#define S5PC1XX_CLKDIV0_APLL_MASK (0x1<<0)
+#define S5PC1XX_CLKDIV0_APLL_SHIFT (0)
+#define S5PC100_CLKDIV0_ARM_MASK (0x7<<4)
+#define S5PC100_CLKDIV0_ARM_SHIFT (4)
+#define S5PC100_CLKDIV0_D0_MASK (0x7<<8)
+#define S5PC100_CLKDIV0_D0_SHIFT (8)
+#define S5PC100_CLKDIV0_PCLKD0_MASK (0x7<<12)
+#define S5PC100_CLKDIV0_PCLKD0_SHIFT (12)
+#define S5PC100_CLKDIV0_SECSS_MASK (0x7<<16)
+#define S5PC100_CLKDIV0_SECSS_SHIFT (16)
+
+/* CLKDIV1 */
+#define S5PC100_CLKDIV1_AM_MASK (0x7<<0)
+#define S5PC100_CLKDIV1_AM_SHIFT (0)
+#define S5PC100_CLKDIV1_MPLL_MASK (0x3<<4)
+#define S5PC100_CLKDIV1_MPLL_SHIFT (4)
+#define S5PC100_CLKDIV1_MPLL2_MASK (0x1<<8)
+#define S5PC100_CLKDIV1_MPLL2_SHIFT (8)
+#define S5PC100_CLKDIV1_D1_MASK (0x7<<12)
+#define S5PC100_CLKDIV1_D1_SHIFT (12)
+#define S5PC100_CLKDIV1_PCLKD1_MASK (0x7<<16)
+#define S5PC100_CLKDIV1_PCLKD1_SHIFT (16)
+#define S5PC100_CLKDIV1_ONENAND_MASK (0x3<<20)
+#define S5PC100_CLKDIV1_ONENAND_SHIFT (20)
+#define S5PC100_CLKDIV1_CAM_MASK (0x1F<<24)
+#define S5PC100_CLKDIV1_CAM_SHIFT (24)
+
+/* CLKDIV2 */
+#define S5PC100_CLKDIV2_UART_MASK (0x7<<0)
+#define S5PC100_CLKDIV2_UART_SHIFT (0)
+#define S5PC100_CLKDIV2_SPI0_MASK (0xf<<4)
+#define S5PC100_CLKDIV2_SPI0_SHIFT (4)
+#define S5PC100_CLKDIV2_SPI1_MASK (0xf<<8)
+#define S5PC100_CLKDIV2_SPI1_SHIFT (8)
+#define S5PC100_CLKDIV2_SPI2_MASK (0xf<<12)
+#define S5PC100_CLKDIV2_SPI2_SHIFT (12)
+#define S5PC100_CLKDIV2_IRDA_MASK (0xf<<16)
+#define S5PC100_CLKDIV2_IRDA_SHIFT (16)
+#define S5PC100_CLKDIV2_UHOST_MASK (0xf<<20)
+#define S5PC100_CLKDIV2_UHOST_SHIFT (20)
+
+/* CLKDIV3 */
+#define S5PC100_CLKDIV3_MMC0_MASK (0xf<<0)
+#define S5PC100_CLKDIV3_MMC0_SHIFT (0)
+#define S5PC100_CLKDIV3_MMC1_MASK (0xf<<4)
+#define S5PC100_CLKDIV3_MMC1_SHIFT (4)
+#define S5PC100_CLKDIV3_MMC2_MASK (0xf<<8)
+#define S5PC100_CLKDIV3_MMC2_SHIFT (8)
+#define S5PC100_CLKDIV3_LCD_MASK (0xf<<12)
+#define S5PC100_CLKDIV3_LCD_SHIFT (12)
+#define S5PC100_CLKDIV3_FIMC0_MASK (0xf<<16)
+#define S5PC100_CLKDIV3_FIMC0_SHIFT (16)
+#define S5PC100_CLKDIV3_FIMC1_MASK (0xf<<20)
+#define S5PC100_CLKDIV3_FIMC1_SHIFT (20)
+#define S5PC100_CLKDIV3_FIMC2_MASK (0xf<<24)
+#define S5PC100_CLKDIV3_FIMC2_SHIFT (24)
+#define S5PC100_CLKDIV3_HDMI_MASK (0xf<<28)
+#define S5PC100_CLKDIV3_HDMI_SHIFT (28)
+
+/* CLKDIV4 */
+#define S5PC100_CLKDIV4_PWI_MASK (0x7<<0)
+#define S5PC100_CLKDIV4_PWI_SHIFT (0)
+#define S5PC100_CLKDIV4_HCLKD2_MASK (0x7<<4)
+#define S5PC100_CLKDIV4_HCLKD2_SHIFT (4)
+#define S5PC100_CLKDIV4_I2SD2_MASK (0xf<<8)
+#define S5PC100_CLKDIV4_I2SD2_SHIFT (8)
+#define S5PC100_CLKDIV4_AUDIO0_MASK (0xf<<12)
+#define S5PC100_CLKDIV4_AUDIO0_SHIFT (12)
+#define S5PC100_CLKDIV4_AUDIO1_MASK (0xf<<16)
+#define S5PC100_CLKDIV4_AUDIO1_SHIFT (16)
+#define S5PC100_CLKDIV4_AUDIO2_MASK (0xf<<20)
+#define S5PC100_CLKDIV4_AUDIO2_SHIFT (20)
+
+
+/* HCLKD0/PCLKD0 Clock Gate 0 Registers */
+#define S5PC100_CLKGATE_D00_INTC (1<<0)
+#define S5PC100_CLKGATE_D00_TZIC (1<<1)
+#define S5PC100_CLKGATE_D00_CFCON (1<<2)
+#define S5PC100_CLKGATE_D00_MDMA (1<<3)
+#define S5PC100_CLKGATE_D00_G2D (1<<4)
+#define S5PC100_CLKGATE_D00_SECSS (1<<5)
+#define S5PC100_CLKGATE_D00_CSSYS (1<<6)
+
+/* HCLKD0/PCLKD0 Clock Gate 1 Registers */
+#define S5PC100_CLKGATE_D01_DMC (1<<0)
+#define S5PC100_CLKGATE_D01_SROMC (1<<1)
+#define S5PC100_CLKGATE_D01_ONENAND (1<<2)
+#define S5PC100_CLKGATE_D01_NFCON (1<<3)
+#define S5PC100_CLKGATE_D01_INTMEM (1<<4)
+#define S5PC100_CLKGATE_D01_EBI (1<<5)
+
+/* PCLKD0 Clock Gate 2 Registers */
+#define S5PC100_CLKGATE_D02_SECKEY (1<<1)
+#define S5PC100_CLKGATE_D02_SDM (1<<2)
+
+/* HCLKD1/PCLKD1 Clock Gate 0 Registers */
+#define S5PC100_CLKGATE_D10_PDMA0 (1<<0)
+#define S5PC100_CLKGATE_D10_PDMA1 (1<<1)
+#define S5PC100_CLKGATE_D10_USBHOST (1<<2)
+#define S5PC100_CLKGATE_D10_USBOTG (1<<3)
+#define S5PC100_CLKGATE_D10_MODEMIF (1<<4)
+#define S5PC100_CLKGATE_D10_HSMMC0 (1<<5)
+#define S5PC100_CLKGATE_D10_HSMMC1 (1<<6)
+#define S5PC100_CLKGATE_D10_HSMMC2 (1<<7)
+
+/* HCLKD1/PCLKD1 Clock Gate 1 Registers */
+#define S5PC100_CLKGATE_D11_LCD (1<<0)
+#define S5PC100_CLKGATE_D11_ROTATOR (1<<1)
+#define S5PC100_CLKGATE_D11_FIMC0 (1<<2)
+#define S5PC100_CLKGATE_D11_FIMC1 (1<<3)
+#define S5PC100_CLKGATE_D11_FIMC2 (1<<4)
+#define S5PC100_CLKGATE_D11_JPEG (1<<5)
+#define S5PC100_CLKGATE_D11_DSI (1<<6)
+#define S5PC100_CLKGATE_D11_CSI (1<<7)
+#define S5PC100_CLKGATE_D11_G3D (1<<8)
+
+/* HCLKD1/PCLKD1 Clock Gate 2 Registers */
+#define S5PC100_CLKGATE_D12_TV (1<<0)
+#define S5PC100_CLKGATE_D12_VP (1<<1)
+#define S5PC100_CLKGATE_D12_MIXER (1<<2)
+#define S5PC100_CLKGATE_D12_HDMI (1<<3)
+#define S5PC100_CLKGATE_D12_MFC (1<<4)
+
+/* HCLKD1/PCLKD1 Clock Gate 3 Registers */
+#define S5PC100_CLKGATE_D13_CHIPID (1<<0)
+#define S5PC100_CLKGATE_D13_GPIO (1<<1)
+#define S5PC100_CLKGATE_D13_APC (1<<2)
+#define S5PC100_CLKGATE_D13_IEC (1<<3)
+#define S5PC100_CLKGATE_D13_PWM (1<<6)
+#define S5PC100_CLKGATE_D13_SYSTIMER (1<<7)
+#define S5PC100_CLKGATE_D13_WDT (1<<8)
+#define S5PC100_CLKGATE_D13_RTC (1<<9)
+
+/* HCLKD1/PCLKD1 Clock Gate 4 Registers */
+#define S5PC100_CLKGATE_D14_UART0 (1<<0)
+#define S5PC100_CLKGATE_D14_UART1 (1<<1)
+#define S5PC100_CLKGATE_D14_UART2 (1<<2)
+#define S5PC100_CLKGATE_D14_UART3 (1<<3)
+#define S5PC100_CLKGATE_D14_IIC (1<<4)
+#define S5PC100_CLKGATE_D14_HDMI_IIC (1<<5)
+#define S5PC100_CLKGATE_D14_SPI0 (1<<6)
+#define S5PC100_CLKGATE_D14_SPI1 (1<<7)
+#define S5PC100_CLKGATE_D14_SPI2 (1<<8)
+#define S5PC100_CLKGATE_D14_IRDA (1<<9)
+#define S5PC100_CLKGATE_D14_CCAN0 (1<<10)
+#define S5PC100_CLKGATE_D14_CCAN1 (1<<11)
+#define S5PC100_CLKGATE_D14_HSITX (1<<12)
+#define S5PC100_CLKGATE_D14_HSIRX (1<<13)
+
+/* HCLKD1/PCLKD1 Clock Gate 5 Registers */
+#define S5PC100_CLKGATE_D15_IIS0 (1<<0)
+#define S5PC100_CLKGATE_D15_IIS1 (1<<1)
+#define S5PC100_CLKGATE_D15_IIS2 (1<<2)
+#define S5PC100_CLKGATE_D15_AC97 (1<<3)
+#define S5PC100_CLKGATE_D15_PCM0 (1<<4)
+#define S5PC100_CLKGATE_D15_PCM1 (1<<5)
+#define S5PC100_CLKGATE_D15_SPDIF (1<<6)
+#define S5PC100_CLKGATE_D15_TSADC (1<<7)
+#define S5PC100_CLKGATE_D15_KEYIF (1<<8)
+#define S5PC100_CLKGATE_D15_CG (1<<9)
+
+/* HCLKD2 Clock Gate 0 Registers */
+#define S5PC100_CLKGATE_D20_HCLKD2 (1<<0)
+#define S5PC100_CLKGATE_D20_I2SD2 (1<<1)
+
+/* Special Clock Gate 0 Registers */
+#define S5PC1XX_CLKGATE_SCLK0_HPM (1<<0)
+#define S5PC1XX_CLKGATE_SCLK0_PWI (1<<1)
+#define S5PC100_CLKGATE_SCLK0_ONENAND (1<<2)
+#define S5PC100_CLKGATE_SCLK0_UART (1<<3)
+#define S5PC100_CLKGATE_SCLK0_SPI0 (1<<4)
+#define S5PC100_CLKGATE_SCLK0_SPI1 (1<<5)
+#define S5PC100_CLKGATE_SCLK0_SPI2 (1<<6)
+#define S5PC100_CLKGATE_SCLK0_SPI0_48 (1<<7)
+#define S5PC100_CLKGATE_SCLK0_SPI1_48 (1<<8)
+#define S5PC100_CLKGATE_SCLK0_SPI2_48 (1<<9)
+#define S5PC100_CLKGATE_SCLK0_IRDA (1<<10)
+#define S5PC100_CLKGATE_SCLK0_USBHOST (1<<11)
+#define S5PC100_CLKGATE_SCLK0_MMC0 (1<<12)
+#define S5PC100_CLKGATE_SCLK0_MMC1 (1<<13)
+#define S5PC100_CLKGATE_SCLK0_MMC2 (1<<14)
+#define S5PC100_CLKGATE_SCLK0_MMC0_48 (1<<15)
+#define S5PC100_CLKGATE_SCLK0_MMC1_48 (1<<16)
+#define S5PC100_CLKGATE_SCLK0_MMC2_48 (1<<17)
+
+/* Special Clock Gate 1 Registers */
+#define S5PC100_CLKGATE_SCLK1_LCD (1<<0)
+#define S5PC100_CLKGATE_SCLK1_FIMC0 (1<<1)
+#define S5PC100_CLKGATE_SCLK1_FIMC1 (1<<2)
+#define S5PC100_CLKGATE_SCLK1_FIMC2 (1<<3)
+#define S5PC100_CLKGATE_SCLK1_TV54 (1<<4)
+#define S5PC100_CLKGATE_SCLK1_VDAC54 (1<<5)
+#define S5PC100_CLKGATE_SCLK1_MIXER (1<<6)
+#define S5PC100_CLKGATE_SCLK1_HDMI (1<<7)
+#define S5PC100_CLKGATE_SCLK1_AUDIO0 (1<<8)
+#define S5PC100_CLKGATE_SCLK1_AUDIO1 (1<<9)
+#define S5PC100_CLKGATE_SCLK1_AUDIO2 (1<<10)
+#define S5PC100_CLKGATE_SCLK1_SPDIF (1<<11)
+#define S5PC100_CLKGATE_SCLK1_CAM (1<<12)
+
+/* register for power management */
+#define S5PC100_PWR_CFG S5PC1XX_CLKREG(0x8000)
+#define S5PC100_EINT_WAKEUP_MASK S5PC1XX_CLKREG(0x8004)
+#define S5PC100_NORMAL_CFG S5PC1XX_CLKREG(0x8010)
+#define S5PC100_STOP_CFG S5PC1XX_CLKREG(0x8014)
+#define S5PC100_SLEEP_CFG S5PC1XX_CLKREG(0x8018)
+#define S5PC100_STOP_MEM_CFG S5PC1XX_CLKREG(0x801C)
+#define S5PC100_OSC_FREQ S5PC1XX_CLKREG(0x8100)
+#define S5PC100_OSC_STABLE S5PC1XX_CLKREG(0x8104)
+#define S5PC100_PWR_STABLE S5PC1XX_CLKREG(0x8108)
+#define S5PC100_MTC_STABLE S5PC1XX_CLKREG(0x8110)
+#define S5PC100_CLAMP_STABLE S5PC1XX_CLKREG(0x8114)
+#define S5PC100_OTHERS S5PC1XX_CLKREG(0x8200)
+#define S5PC100_RST_STAT S5PC1XX_CLKREG(0x8300)
+#define S5PC100_WAKEUP_STAT S5PC1XX_CLKREG(0x8304)
+#define S5PC100_BLK_PWR_STAT S5PC1XX_CLKREG(0x8308)
+#define S5PC100_INFORM0 S5PC1XX_CLKREG(0x8400)
+#define S5PC100_INFORM1 S5PC1XX_CLKREG(0x8404)
+#define S5PC100_INFORM2 S5PC1XX_CLKREG(0x8408)
+#define S5PC100_INFORM3 S5PC1XX_CLKREG(0x840C)
+#define S5PC100_INFORM4 S5PC1XX_CLKREG(0x8410)
+#define S5PC100_INFORM5 S5PC1XX_CLKREG(0x8414)
+#define S5PC100_INFORM6 S5PC1XX_CLKREG(0x8418)
+#define S5PC100_INFORM7 S5PC1XX_CLKREG(0x841C)
+#define S5PC100_DCGIDX_MAP0 S5PC1XX_CLKREG(0x8500)
+#define S5PC100_DCGIDX_MAP1 S5PC1XX_CLKREG(0x8504)
+#define S5PC100_DCGIDX_MAP2 S5PC1XX_CLKREG(0x8508)
+#define S5PC100_DCGPERF_MAP0 S5PC1XX_CLKREG(0x850C)
+#define S5PC100_DCGPERF_MAP1 S5PC1XX_CLKREG(0x8510)
+#define S5PC100_DVCIDX_MAP S5PC1XX_CLKREG(0x8514)
+#define S5PC100_FREQ_CPU S5PC1XX_CLKREG(0x8518)
+#define S5PC100_FREQ_DPM S5PC1XX_CLKREG(0x851C)
+#define S5PC100_DVSEMCLK_EN S5PC1XX_CLKREG(0x8520)
+#define S5PC100_APLL_CON_L8 S5PC1XX_CLKREG(0x8600)
+#define S5PC100_APLL_CON_L7 S5PC1XX_CLKREG(0x8604)
+#define S5PC100_APLL_CON_L6 S5PC1XX_CLKREG(0x8608)
+#define S5PC100_APLL_CON_L5 S5PC1XX_CLKREG(0x860C)
+#define S5PC100_APLL_CON_L4 S5PC1XX_CLKREG(0x8610)
+#define S5PC100_APLL_CON_L3 S5PC1XX_CLKREG(0x8614)
+#define S5PC100_APLL_CON_L2 S5PC1XX_CLKREG(0x8618)
+#define S5PC100_APLL_CON_L1 S5PC1XX_CLKREG(0x861C)
+#define S5PC100_IEM_CONTROL S5PC1XX_CLKREG(0x8620)
+#define S5PC100_CLKDIV_IEM_L8 S5PC1XX_CLKREG(0x8700)
+#define S5PC100_CLKDIV_IEM_L7 S5PC1XX_CLKREG(0x8704)
+#define S5PC100_CLKDIV_IEM_L6 S5PC1XX_CLKREG(0x8708)
+#define S5PC100_CLKDIV_IEM_L5 S5PC1XX_CLKREG(0x870C)
+#define S5PC100_CLKDIV_IEM_L4 S5PC1XX_CLKREG(0x8710)
+#define S5PC100_CLKDIV_IEM_L3 S5PC1XX_CLKREG(0x8714)
+#define S5PC100_CLKDIV_IEM_L2 S5PC1XX_CLKREG(0x8718)
+#define S5PC100_CLKDIV_IEM_L1 S5PC1XX_CLKREG(0x871C)
+#define S5PC100_IEM_HPMCLK_DIV S5PC1XX_CLKREG(0x8724)
+
+#define S5PC100_SWRESET S5PC1XX_CLKREG(0x100000)
+#define S5PC100_OND_SWRESET S5PC1XX_CLKREG(0x100008)
+#define S5PC100_GEN_CTRL S5PC1XX_CLKREG(0x100100)
+#define S5PC100_GEN_STATUS S5PC1XX_CLKREG(0x100104)
+#define S5PC100_MEM_SYS_CFG S5PC1XX_CLKREG(0x100200)
+#define S5PC100_CAM_MUX_SEL S5PC1XX_CLKREG(0x100300)
+#define S5PC100_MIXER_OUT_SEL S5PC1XX_CLKREG(0x100304)
+#define S5PC100_LPMP_MODE_SEL S5PC1XX_CLKREG(0x100308)
+#define S5PC100_MIPI_PHY_CON0 S5PC1XX_CLKREG(0x100400)
+#define S5PC100_MIPI_PHY_CON1 S5PC1XX_CLKREG(0x100414)
+#define S5PC100_HDMI_PHY_CON0 S5PC1XX_CLKREG(0x100420)
+
+#define S5PC100_CFG_WFI_CLEAN (~(3<<5))
+#define S5PC100_CFG_WFI_IDLE (1<<5)
+#define S5PC100_CFG_WFI_STOP (2<<5)
+#define S5PC100_CFG_WFI_SLEEP (3<<5)
+
+#define S5PC100_OTHER_SYS_INT 24
+#define S5PC100_OTHER_STA_TYPE 23
+#define STA_TYPE_EXPON 0
+#define STA_TYPE_SFR 1
+
+#define S5PC100_PWR_STA_EXP_SCALE 0
+#define S5PC100_PWR_STA_CNT 4
+
+#define S5PC100_PWR_STABLE_COUNT 85500
+
+#define S5PC100_SLEEP_CFG_OSC_EN 0
+
+/* OTHERS Resgister */
+#define S5PC100_OTHERS_USB_SIG_MASK (1 << 16)
+#define S5PC100_OTHERS_MIPI_DPHY_EN (1 << 28)
+
+/* MIPI D-PHY Control Register 0 */
+#define S5PC100_MIPI_PHY_CON0_M_RESETN (1 << 1)
+#define S5PC100_MIPI_PHY_CON0_S_RESETN (1 << 0)
+
+#endif /* _PLAT_REGS_CLOCK_H */
diff --git a/arch/arm/plat-s5pc1xx/include/plat/s5pc100.h b/arch/arm/plat-s5pc1xx/include/plat/s5pc100.h
new file mode 100644
index 00000000000..45e27513166
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/include/plat/s5pc100.h
@@ -0,0 +1,65 @@
+/* arch/arm/plat-s5pc1xx/include/plat/s5pc100.h
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * Header file for s5pc100 cpu support
+ *
+ * Based on plat-s3c64xx/include/plat/s3c6400.h
+ *
+ * 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.
+*/
+
+/* Common init code for S5PC100 related SoCs */
+extern int s5pc100_init(void);
+extern void s5pc100_map_io(void);
+extern void s5pc100_init_clocks(int xtal);
+extern int s5pc100_register_baseclocks(unsigned long xtal);
+extern void s5pc100_init_irq(void);
+extern void s5pc100_init_io(struct map_desc *mach_desc, int size);
+extern void s5pc100_common_init_uarts(struct s3c2410_uartcfg *cfg, int no);
+extern void s5pc100_register_clocks(void);
+extern void s5pc100_setup_clocks(void);
+extern struct sysdev_class s5pc100_sysclass;
+
+#define s5pc100_init_uarts s5pc100_common_init_uarts
+
+/* Some day, belows will be moved to plat-s5pc/include/plat/cpu.h */
+extern void s5pc1xx_init_irq(u32 *vic_valid, int num);
+extern void s5pc1xx_init_io(struct map_desc *mach_desc, int size);
+
+/* Some day, belows will be moved to plat-s5pc/include/plat/clock.h */
+extern struct clk clk_hpll;
+extern struct clk clk_hd0;
+extern struct clk clk_pd0;
+extern struct clk clk_54m;
+extern struct clk clk_dout_mpll2;
+extern void s5pc1xx_register_clocks(void);
+extern int s5pc1xx_sclk0_ctrl(struct clk *clk, int enable);
+extern int s5pc1xx_sclk1_ctrl(struct clk *clk, int enable);
+
+/* Some day, belows will be moved to plat-s5pc/include/plat/devs.h */
+extern struct s3c24xx_uart_resources s5pc1xx_uart_resources[];
+extern struct platform_device s3c_device_g2d;
+extern struct platform_device s3c_device_g3d;
+extern struct platform_device s3c_device_vpp;
+extern struct platform_device s3c_device_tvenc;
+extern struct platform_device s3c_device_tvscaler;
+extern struct platform_device s3c_device_rotator;
+extern struct platform_device s3c_device_jpeg;
+extern struct platform_device s3c_device_onenand;
+extern struct platform_device s3c_device_usb_otghcd;
+extern struct platform_device s3c_device_keypad;
+extern struct platform_device s3c_device_ts;
+extern struct platform_device s3c_device_g3d;
+extern struct platform_device s3c_device_smc911x;
+extern struct platform_device s3c_device_fimc0;
+extern struct platform_device s3c_device_fimc1;
+extern struct platform_device s3c_device_mfc;
+extern struct platform_device s3c_device_ac97;
+extern struct platform_device s3c_device_fimc0;
+extern struct platform_device s3c_device_fimc1;
+extern struct platform_device s3c_device_fimc2;
+
diff --git a/arch/arm/plat-s5pc1xx/irq.c b/arch/arm/plat-s5pc1xx/irq.c
new file mode 100644
index 00000000000..80d6dd942cb
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/irq.c
@@ -0,0 +1,259 @@
+/* arch/arm/plat-s5pc1xx/irq.c
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC1XX - Interrupt handling
+ *
+ * Based on plat-s3c64xx/irq.c
+ *
+ * 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/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+
+#include <asm/hardware/vic.h>
+
+#include <mach/map.h>
+#include <plat/regs-timer.h>
+#include <plat/cpu.h>
+
+/* Timer interrupt handling */
+
+static void s3c_irq_demux_timer(unsigned int base_irq, unsigned int sub_irq)
+{
+ generic_handle_irq(sub_irq);
+}
+
+static void s3c_irq_demux_timer0(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_timer(irq, IRQ_TIMER0);
+}
+
+static void s3c_irq_demux_timer1(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_timer(irq, IRQ_TIMER1);
+}
+
+static void s3c_irq_demux_timer2(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_timer(irq, IRQ_TIMER2);
+}
+
+static void s3c_irq_demux_timer3(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_timer(irq, IRQ_TIMER3);
+}
+
+static void s3c_irq_demux_timer4(unsigned int irq, struct irq_desc *desc)
+{
+ s3c_irq_demux_timer(irq, IRQ_TIMER4);
+}
+
+/* We assume the IRQ_TIMER0..IRQ_TIMER4 range is continuous. */
+
+static void s3c_irq_timer_mask(unsigned int irq)
+{
+ u32 reg = __raw_readl(S3C64XX_TINT_CSTAT);
+
+ reg &= 0x1f; /* mask out pending interrupts */
+ reg &= ~(1 << (irq - IRQ_TIMER0));
+ __raw_writel(reg, S3C64XX_TINT_CSTAT);
+}
+
+static void s3c_irq_timer_unmask(unsigned int irq)
+{
+ u32 reg = __raw_readl(S3C64XX_TINT_CSTAT);
+
+ reg &= 0x1f; /* mask out pending interrupts */
+ reg |= 1 << (irq - IRQ_TIMER0);
+ __raw_writel(reg, S3C64XX_TINT_CSTAT);
+}
+
+static void s3c_irq_timer_ack(unsigned int irq)
+{
+ u32 reg = __raw_readl(S3C64XX_TINT_CSTAT);
+
+ reg &= 0x1f;
+ reg |= (1 << 5) << (irq - IRQ_TIMER0);
+ __raw_writel(reg, S3C64XX_TINT_CSTAT);
+}
+
+static struct irq_chip s3c_irq_timer = {
+ .name = "s3c-timer",
+ .mask = s3c_irq_timer_mask,
+ .unmask = s3c_irq_timer_unmask,
+ .ack = s3c_irq_timer_ack,
+};
+
+struct uart_irq {
+ void __iomem *regs;
+ unsigned int base_irq;
+ unsigned int parent_irq;
+};
+
+/* Note, we make use of the fact that the parent IRQs, IRQ_UART[0..3]
+ * are consecutive when looking up the interrupt in the demux routines.
+ */
+static struct uart_irq uart_irqs[] = {
+ [0] = {
+ .regs = (void *)S3C_VA_UART0,
+ .base_irq = IRQ_S3CUART_BASE0,
+ .parent_irq = IRQ_UART0,
+ },
+ [1] = {
+ .regs = (void *)S3C_VA_UART1,
+ .base_irq = IRQ_S3CUART_BASE1,
+ .parent_irq = IRQ_UART1,
+ },
+ [2] = {
+ .regs = (void *)S3C_VA_UART2,
+ .base_irq = IRQ_S3CUART_BASE2,
+ .parent_irq = IRQ_UART2,
+ },
+ [3] = {
+ .regs = (void *)S3C_VA_UART3,
+ .base_irq = IRQ_S3CUART_BASE3,
+ .parent_irq = IRQ_UART3,
+ },
+};
+
+static inline void __iomem *s3c_irq_uart_base(unsigned int irq)
+{
+ struct uart_irq *uirq = get_irq_chip_data(irq);
+ return uirq->regs;
+}
+
+static inline unsigned int s3c_irq_uart_bit(unsigned int irq)
+{
+ return irq & 3;
+}
+
+/* UART interrupt registers, not worth adding to seperate include header */
+#define S3C64XX_UINTP 0x30
+#define S3C64XX_UINTSP 0x34
+#define S3C64XX_UINTM 0x38
+
+static void s3c_irq_uart_mask(unsigned int irq)
+{
+ void __iomem *regs = s3c_irq_uart_base(irq);
+ unsigned int bit = s3c_irq_uart_bit(irq);
+ u32 reg;
+
+ reg = __raw_readl(regs + S3C64XX_UINTM);
+ reg |= (1 << bit);
+ __raw_writel(reg, regs + S3C64XX_UINTM);
+}
+
+static void s3c_irq_uart_maskack(unsigned int irq)
+{
+ void __iomem *regs = s3c_irq_uart_base(irq);
+ unsigned int bit = s3c_irq_uart_bit(irq);
+ u32 reg;
+
+ reg = __raw_readl(regs + S3C64XX_UINTM);
+ reg |= (1 << bit);
+ __raw_writel(reg, regs + S3C64XX_UINTM);
+ __raw_writel(1 << bit, regs + S3C64XX_UINTP);
+}
+
+static void s3c_irq_uart_unmask(unsigned int irq)
+{
+ void __iomem *regs = s3c_irq_uart_base(irq);
+ unsigned int bit = s3c_irq_uart_bit(irq);
+ u32 reg;
+
+ reg = __raw_readl(regs + S3C64XX_UINTM);
+ reg &= ~(1 << bit);
+ __raw_writel(reg, regs + S3C64XX_UINTM);
+}
+
+static void s3c_irq_uart_ack(unsigned int irq)
+{
+ void __iomem *regs = s3c_irq_uart_base(irq);
+ unsigned int bit = s3c_irq_uart_bit(irq);
+
+ __raw_writel(1 << bit, regs + S3C64XX_UINTP);
+}
+
+static void s3c_irq_demux_uart(unsigned int irq, struct irq_desc *desc)
+{
+ struct uart_irq *uirq = &uart_irqs[irq - IRQ_UART0];
+ u32 pend = __raw_readl(uirq->regs + S3C64XX_UINTP);
+ int base = uirq->base_irq;
+
+ if (pend & (1 << 0))
+ generic_handle_irq(base);
+ if (pend & (1 << 1))
+ generic_handle_irq(base + 1);
+ if (pend & (1 << 2))
+ generic_handle_irq(base + 2);
+ if (pend & (1 << 3))
+ generic_handle_irq(base + 3);
+}
+
+static struct irq_chip s3c_irq_uart = {
+ .name = "s3c-uart",
+ .mask = s3c_irq_uart_mask,
+ .unmask = s3c_irq_uart_unmask,
+ .mask_ack = s3c_irq_uart_maskack,
+ .ack = s3c_irq_uart_ack,
+};
+
+static void __init s5pc1xx_uart_irq(struct uart_irq *uirq)
+{
+ void __iomem *reg_base = uirq->regs;
+ unsigned int irq;
+ int offs;
+
+ /* mask all interrupts at the start. */
+ __raw_writel(0xf, reg_base + S3C64XX_UINTM);
+
+ for (offs = 0; offs < 3; offs++) {
+ irq = uirq->base_irq + offs;
+
+ set_irq_chip(irq, &s3c_irq_uart);
+ set_irq_chip_data(irq, uirq);
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ set_irq_chained_handler(uirq->parent_irq, s3c_irq_demux_uart);
+}
+
+void __init s5pc1xx_init_irq(u32 *vic_valid, int num)
+{
+ int i;
+ int uart, irq;
+
+ printk(KERN_DEBUG "%s: initialising interrupts\n", __func__);
+
+ /* initialise the pair of VICs */
+ for (i = 0; i < num; i++)
+ vic_init((void *)S5PC1XX_VA_VIC(i), S3C_IRQ(i * S3C_IRQ_OFFSET),
+ vic_valid[i], 0);
+
+ /* add the timer sub-irqs */
+
+ set_irq_chained_handler(IRQ_TIMER0, s3c_irq_demux_timer0);
+ set_irq_chained_handler(IRQ_TIMER1, s3c_irq_demux_timer1);
+ set_irq_chained_handler(IRQ_TIMER2, s3c_irq_demux_timer2);
+ set_irq_chained_handler(IRQ_TIMER3, s3c_irq_demux_timer3);
+ set_irq_chained_handler(IRQ_TIMER4, s3c_irq_demux_timer4);
+
+ for (irq = IRQ_TIMER0; irq <= IRQ_TIMER4; irq++) {
+ set_irq_chip(irq, &s3c_irq_timer);
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID);
+ }
+
+ for (uart = 0; uart < ARRAY_SIZE(uart_irqs); uart++)
+ s5pc1xx_uart_irq(&uart_irqs[uart]);
+}
+
+
diff --git a/arch/arm/plat-s5pc1xx/s5pc100-clock.c b/arch/arm/plat-s5pc1xx/s5pc100-clock.c
new file mode 100644
index 00000000000..6b24035172f
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/s5pc100-clock.c
@@ -0,0 +1,1139 @@
+/* linux/arch/arm/plat-s5pc1xx/s5pc100-clock.c
+ *
+ * Copyright 2009 Samsung Electronics, Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC100 based common clock support
+ *
+ * Based on plat-s3c64xx/s3c6400-clock.c
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/sysdev.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/map.h>
+
+#include <plat/cpu-freq.h>
+
+#include <plat/regs-clock.h>
+#include <plat/clock.h>
+#include <plat/cpu.h>
+#include <plat/pll.h>
+#include <plat/devs.h>
+#include <plat/s5pc100.h>
+
+/* fin_apll, fin_mpll and fin_epll are all the same clock, which we call
+ * ext_xtal_mux for want of an actual name from the manual.
+*/
+
+static struct clk clk_ext_xtal_mux = {
+ .name = "ext_xtal",
+ .id = -1,
+};
+
+#define clk_fin_apll clk_ext_xtal_mux
+#define clk_fin_mpll clk_ext_xtal_mux
+#define clk_fin_epll clk_ext_xtal_mux
+#define clk_fin_hpll clk_ext_xtal_mux
+
+#define clk_fout_mpll clk_mpll
+
+struct clk_sources {
+ unsigned int nr_sources;
+ struct clk **sources;
+};
+
+struct clksrc_clk {
+ struct clk clk;
+ unsigned int mask;
+ unsigned int shift;
+
+ struct clk_sources *sources;
+
+ unsigned int divider_shift;
+ void __iomem *reg_divider;
+ void __iomem *reg_source;
+};
+
+static int clk_default_setrate(struct clk *clk, unsigned long rate)
+{
+ clk->rate = rate;
+ return 1;
+}
+
+struct clk clk_27m = {
+ .name = "clk_27m",
+ .id = -1,
+ .rate = 27000000,
+};
+
+static int clk_48m_ctrl(struct clk *clk, int enable)
+{
+ unsigned long flags;
+ u32 val;
+
+ /* can't rely on clock lock, this register has other usages */
+ local_irq_save(flags);
+
+ val = __raw_readl(S5PC1XX_CLK_SRC1);
+ if (enable)
+ val |= S5PC100_CLKSRC1_CLK48M_MASK;
+ else
+ val &= ~S5PC100_CLKSRC1_CLK48M_MASK;
+
+ __raw_writel(val, S5PC1XX_CLK_SRC1);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+struct clk clk_48m = {
+ .name = "clk_48m",
+ .id = -1,
+ .rate = 48000000,
+ .enable = clk_48m_ctrl,
+};
+
+struct clk clk_54m = {
+ .name = "clk_54m",
+ .id = -1,
+ .rate = 54000000,
+};
+
+struct clk clk_hpll = {
+ .name = "hpll",
+ .id = -1,
+};
+
+struct clk clk_hd0 = {
+ .name = "hclkd0",
+ .id = -1,
+ .rate = 0,
+ .parent = NULL,
+ .ctrlbit = 0,
+ .set_rate = clk_default_setrate,
+};
+
+struct clk clk_pd0 = {
+ .name = "pclkd0",
+ .id = -1,
+ .rate = 0,
+ .parent = NULL,
+ .ctrlbit = 0,
+ .set_rate = clk_default_setrate,
+};
+
+static int s5pc1xx_clk_gate(void __iomem *reg,
+ struct clk *clk,
+ int enable)
+{
+ unsigned int ctrlbit = clk->ctrlbit;
+ u32 con;
+
+ con = __raw_readl(reg);
+
+ if (enable)
+ con |= ctrlbit;
+ else
+ con &= ~ctrlbit;
+
+ __raw_writel(con, reg);
+ return 0;
+}
+
+static int s5pc1xx_clk_d00_ctrl(struct clk *clk, int enable)
+{
+ return s5pc1xx_clk_gate(S5PC100_CLKGATE_D00, clk, enable);
+}
+
+static int s5pc1xx_clk_d01_ctrl(struct clk *clk, int enable)
+{
+ return s5pc1xx_clk_gate(S5PC100_CLKGATE_D01, clk, enable);
+}
+
+static int s5pc1xx_clk_d02_ctrl(struct clk *clk, int enable)
+{
+ return s5pc1xx_clk_gate(S5PC100_CLKGATE_D02, clk, enable);
+}
+
+static int s5pc1xx_clk_d10_ctrl(struct clk *clk, int enable)
+{
+ return s5pc1xx_clk_gate(S5PC100_CLKGATE_D10, clk, enable);
+}
+
+static int s5pc1xx_clk_d11_ctrl(struct clk *clk, int enable)
+{
+ return s5pc1xx_clk_gate(S5PC100_CLKGATE_D11, clk, enable);
+}
+
+static int s5pc1xx_clk_d12_ctrl(struct clk *clk, int enable)
+{
+ return s5pc1xx_clk_gate(S5PC100_CLKGATE_D12, clk, enable);
+}
+
+static int s5pc1xx_clk_d13_ctrl(struct clk *clk, int enable)
+{
+ return s5pc1xx_clk_gate(S5PC100_CLKGATE_D13, clk, enable);
+}
+
+static int s5pc1xx_clk_d14_ctrl(struct clk *clk, int enable)
+{
+ return s5pc1xx_clk_gate(S5PC100_CLKGATE_D14, clk, enable);
+}
+
+static int s5pc1xx_clk_d15_ctrl(struct clk *clk, int enable)
+{
+ return s5pc1xx_clk_gate(S5PC100_CLKGATE_D15, clk, enable);
+}
+
+static int s5pc1xx_clk_d20_ctrl(struct clk *clk, int enable)
+{
+ return s5pc1xx_clk_gate(S5PC100_CLKGATE_D20, clk, enable);
+}
+
+int s5pc1xx_sclk0_ctrl(struct clk *clk, int enable)
+{
+ return s5pc1xx_clk_gate(S5PC100_SCLKGATE0, clk, enable);
+}
+
+int s5pc1xx_sclk1_ctrl(struct clk *clk, int enable)
+{
+ return s5pc1xx_clk_gate(S5PC100_SCLKGATE1, clk, enable);
+}
+
+static struct clk init_clocks_disable[] = {
+ {
+ .name = "dsi",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d11_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D11_DSI,
+ }, {
+ .name = "csi",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d11_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D11_CSI,
+ }, {
+ .name = "ccan0",
+ .id = 0,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_CCAN0,
+ }, {
+ .name = "ccan1",
+ .id = 1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_CCAN1,
+ }, {
+ .name = "keypad",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d15_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D15_KEYIF,
+ }, {
+ .name = "hclkd2",
+ .id = -1,
+ .parent = NULL,
+ .enable = s5pc1xx_clk_d20_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D20_HCLKD2,
+ }, {
+ .name = "iis-d2",
+ .id = -1,
+ .parent = NULL,
+ .enable = s5pc1xx_clk_d20_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D20_I2SD2,
+ }, {
+ .name = "otg",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d10_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D10_USBOTG,
+ },
+};
+
+static struct clk init_clocks[] = {
+ /* System1 (D0_0) devices */
+ {
+ .name = "intc",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d00_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D00_INTC,
+ }, {
+ .name = "tzic",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d00_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D00_TZIC,
+ }, {
+ .name = "cf-ata",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d00_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D00_CFCON,
+ }, {
+ .name = "mdma",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d00_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D00_MDMA,
+ }, {
+ .name = "g2d",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d00_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D00_G2D,
+ }, {
+ .name = "secss",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d00_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D00_SECSS,
+ }, {
+ .name = "cssys",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d00_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D00_CSSYS,
+ },
+
+ /* Memory (D0_1) devices */
+ {
+ .name = "dmc",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d01_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D01_DMC,
+ }, {
+ .name = "sromc",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d01_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D01_SROMC,
+ }, {
+ .name = "onenand",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d01_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D01_ONENAND,
+ }, {
+ .name = "nand",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d01_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D01_NFCON,
+ }, {
+ .name = "intmem",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d01_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D01_INTMEM,
+ }, {
+ .name = "ebi",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d01_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D01_EBI,
+ },
+
+ /* System2 (D0_2) devices */
+ {
+ .name = "seckey",
+ .id = -1,
+ .parent = &clk_pd0,
+ .enable = s5pc1xx_clk_d02_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D02_SECKEY,
+ }, {
+ .name = "sdm",
+ .id = -1,
+ .parent = &clk_hd0,
+ .enable = s5pc1xx_clk_d02_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D02_SDM,
+ },
+
+ /* File (D1_0) devices */
+ {
+ .name = "pdma0",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d10_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D10_PDMA0,
+ }, {
+ .name = "pdma1",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d10_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D10_PDMA1,
+ }, {
+ .name = "usb-host",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d10_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D10_USBHOST,
+ }, {
+ .name = "modem",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d10_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D10_MODEMIF,
+ }, {
+ .name = "hsmmc",
+ .id = 0,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d10_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D10_HSMMC0,
+ }, {
+ .name = "hsmmc",
+ .id = 1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d10_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D10_HSMMC1,
+ }, {
+ .name = "hsmmc",
+ .id = 2,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d10_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D10_HSMMC2,
+ },
+
+ /* Multimedia1 (D1_1) devices */
+ {
+ .name = "lcd",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d11_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D11_LCD,
+ }, {
+ .name = "rotator",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d11_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D11_ROTATOR,
+ }, {
+ .name = "fimc",
+ .id = 0,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d11_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D11_FIMC0,
+ }, {
+ .name = "fimc",
+ .id = 1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d11_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D11_FIMC1,
+ }, {
+ .name = "fimc",
+ .id = 2,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d11_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D11_FIMC2,
+ }, {
+ .name = "jpeg",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d11_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D11_JPEG,
+ }, {
+ .name = "g3d",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d11_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D11_G3D,
+ },
+
+ /* Multimedia2 (D1_2) devices */
+ {
+ .name = "tv",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d12_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D12_TV,
+ }, {
+ .name = "vp",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d12_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D12_VP,
+ }, {
+ .name = "mixer",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d12_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D12_MIXER,
+ }, {
+ .name = "hdmi",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d12_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D12_HDMI,
+ }, {
+ .name = "mfc",
+ .id = -1,
+ .parent = &clk_h,
+ .enable = s5pc1xx_clk_d12_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D12_MFC,
+ },
+
+ /* System (D1_3) devices */
+ {
+ .name = "chipid",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d13_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D13_CHIPID,
+ }, {
+ .name = "gpio",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d13_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D13_GPIO,
+ }, {
+ .name = "apc",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d13_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D13_APC,
+ }, {
+ .name = "iec",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d13_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D13_IEC,
+ }, {
+ .name = "timers",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d13_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D13_PWM,
+ }, {
+ .name = "systimer",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d13_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D13_SYSTIMER,
+ }, {
+ .name = "watchdog",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d13_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D13_WDT,
+ }, {
+ .name = "rtc",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d13_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D13_RTC,
+ },
+
+ /* Connectivity (D1_4) devices */
+ {
+ .name = "uart",
+ .id = 0,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_UART0,
+ }, {
+ .name = "uart",
+ .id = 1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_UART1,
+ }, {
+ .name = "uart",
+ .id = 2,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_UART2,
+ }, {
+ .name = "uart",
+ .id = 3,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_UART3,
+ }, {
+ .name = "i2c",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_IIC,
+ }, {
+ .name = "hdmi-i2c",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_HDMI_IIC,
+ }, {
+ .name = "spi",
+ .id = 0,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_SPI0,
+ }, {
+ .name = "spi",
+ .id = 1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_SPI1,
+ }, {
+ .name = "spi",
+ .id = 2,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_SPI2,
+ }, {
+ .name = "irda",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_IRDA,
+ }, {
+ .name = "hsitx",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_HSITX,
+ }, {
+ .name = "hsirx",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d14_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D14_HSIRX,
+ },
+
+ /* Audio (D1_5) devices */
+ {
+ .name = "iis",
+ .id = 0,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d15_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D15_IIS0,
+ }, {
+ .name = "iis",
+ .id = 1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d15_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D15_IIS1,
+ }, {
+ .name = "iis",
+ .id = 2,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d15_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D15_IIS2,
+ }, {
+ .name = "ac97",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d15_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D15_AC97,
+ }, {
+ .name = "pcm",
+ .id = 0,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d15_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D15_PCM0,
+ }, {
+ .name = "pcm",
+ .id = 1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d15_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D15_PCM1,
+ }, {
+ .name = "spdif",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d15_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D15_SPDIF,
+ }, {
+ .name = "adc",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d15_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D15_TSADC,
+ }, {
+ .name = "keyif",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d15_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D15_KEYIF,
+ }, {
+ .name = "cg",
+ .id = -1,
+ .parent = &clk_p,
+ .enable = s5pc1xx_clk_d15_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_D15_CG,
+ },
+
+ /* Audio (D2_0) devices: all disabled */
+
+ /* Special Clocks 1 */
+ {
+ .name = "sclk_hpm",
+ .id = -1,
+ .parent = NULL,
+ .enable = s5pc1xx_sclk0_ctrl,
+ .ctrlbit = S5PC1XX_CLKGATE_SCLK0_HPM,
+ }, {
+ .name = "sclk_onenand",
+ .id = -1,
+ .parent = NULL,
+ .enable = s5pc1xx_sclk0_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_SCLK0_ONENAND,
+ }, {
+ .name = "sclk_spi_48",
+ .id = 0,
+ .parent = &clk_48m,
+ .enable = s5pc1xx_sclk0_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI0_48,
+ }, {
+ .name = "sclk_spi_48",
+ .id = 1,
+ .parent = &clk_48m,
+ .enable = s5pc1xx_sclk0_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI1_48,
+ }, {
+ .name = "sclk_spi_48",
+ .id = 2,
+ .parent = &clk_48m,
+ .enable = s5pc1xx_sclk0_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_SCLK0_SPI2_48,
+ }, {
+ .name = "sclk_mmc_48",
+ .id = 0,
+ .parent = &clk_48m,
+ .enable = s5pc1xx_sclk0_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC0_48,
+ }, {
+ .name = "sclk_mmc_48",
+ .id = 1,
+ .parent = &clk_48m,
+ .enable = s5pc1xx_sclk0_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC1_48,
+ }, {
+ .name = "sclk_mmc_48",
+ .id = 2,
+ .parent = &clk_48m,
+ .enable = s5pc1xx_sclk0_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_SCLK0_MMC2_48,
+ },
+
+ /* Special Clocks 2 */
+ {
+ .name = "sclk_tv_54",
+ .id = -1,
+ .parent = &clk_54m,
+ .enable = s5pc1xx_sclk1_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_SCLK1_TV54,
+ }, {
+ .name = "sclk_vdac_54",
+ .id = -1,
+ .parent = &clk_54m,
+ .enable = s5pc1xx_sclk1_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_SCLK1_VDAC54,
+ }, {
+ .name = "sclk_spdif",
+ .id = -1,
+ .parent = NULL,
+ .enable = s5pc1xx_sclk1_ctrl,
+ .ctrlbit = S5PC100_CLKGATE_SCLK1_SPDIF,
+ },
+};
+
+void __init s5pc1xx_register_clocks(void)
+{
+ struct clk *clkp;
+ int ret;
+ int ptr;
+
+ clkp = init_clocks;
+ for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
+ ret = s3c24xx_register_clock(clkp);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to register clock %s (%d)\n",
+ clkp->name, ret);
+ }
+ }
+
+ clkp = init_clocks_disable;
+ for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
+
+ ret = s3c24xx_register_clock(clkp);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to register clock %s (%d)\n",
+ clkp->name, ret);
+ }
+
+ (clkp->enable)(clkp, 0);
+ }
+
+ s3c_pwmclk_init();
+}
+static struct clk clk_fout_apll = {
+ .name = "fout_apll",
+ .id = -1,
+};
+
+static struct clk *clk_src_apll_list[] = {
+ [0] = &clk_fin_apll,
+ [1] = &clk_fout_apll,
+};
+
+static struct clk_sources clk_src_apll = {
+ .sources = clk_src_apll_list,
+ .nr_sources = ARRAY_SIZE(clk_src_apll_list),
+};
+
+static struct clksrc_clk clk_mout_apll = {
+ .clk = {
+ .name = "mout_apll",
+ .id = -1,
+ },
+ .shift = S5PC1XX_CLKSRC0_APLL_SHIFT,
+ .mask = S5PC1XX_CLKSRC0_APLL_MASK,
+ .sources = &clk_src_apll,
+ .reg_source = S5PC1XX_CLK_SRC0,
+};
+
+static struct clk clk_fout_epll = {
+ .name = "fout_epll",
+ .id = -1,
+};
+
+static struct clk *clk_src_epll_list[] = {
+ [0] = &clk_fin_epll,
+ [1] = &clk_fout_epll,
+};
+
+static struct clk_sources clk_src_epll = {
+ .sources = clk_src_epll_list,
+ .nr_sources = ARRAY_SIZE(clk_src_epll_list),
+};
+
+static struct clksrc_clk clk_mout_epll = {
+ .clk = {
+ .name = "mout_epll",
+ .id = -1,
+ },
+ .shift = S5PC1XX_CLKSRC0_EPLL_SHIFT,
+ .mask = S5PC1XX_CLKSRC0_EPLL_MASK,
+ .sources = &clk_src_epll,
+ .reg_source = S5PC1XX_CLK_SRC0,
+};
+
+static struct clk *clk_src_mpll_list[] = {
+ [0] = &clk_fin_mpll,
+ [1] = &clk_fout_mpll,
+};
+
+static struct clk_sources clk_src_mpll = {
+ .sources = clk_src_mpll_list,
+ .nr_sources = ARRAY_SIZE(clk_src_mpll_list),
+};
+
+static struct clksrc_clk clk_mout_mpll = {
+ .clk = {
+ .name = "mout_mpll",
+ .id = -1,
+ },
+ .shift = S5PC1XX_CLKSRC0_MPLL_SHIFT,
+ .mask = S5PC1XX_CLKSRC0_MPLL_MASK,
+ .sources = &clk_src_mpll,
+ .reg_source = S5PC1XX_CLK_SRC0,
+};
+
+static unsigned long s5pc1xx_clk_doutmpll_get_rate(struct clk *clk)
+{
+ unsigned long rate = clk_get_rate(clk->parent);
+ unsigned long clkdiv;
+
+ printk(KERN_DEBUG "%s: parent is %ld\n", __func__, rate);
+
+ clkdiv = __raw_readl(S5PC1XX_CLK_DIV1) & S5PC100_CLKDIV1_MPLL_MASK;
+ rate /= (clkdiv >> S5PC100_CLKDIV1_MPLL_SHIFT) + 1;
+
+ return rate;
+}
+
+static struct clk clk_dout_mpll = {
+ .name = "dout_mpll",
+ .id = -1,
+ .parent = &clk_mout_mpll.clk,
+ .get_rate = s5pc1xx_clk_doutmpll_get_rate,
+};
+
+static unsigned long s5pc1xx_clk_doutmpll2_get_rate(struct clk *clk)
+{
+ unsigned long rate = clk_get_rate(clk->parent);
+ unsigned long clkdiv;
+
+ printk(KERN_DEBUG "%s: parent is %ld\n", __func__, rate);
+
+ clkdiv = __raw_readl(S5PC1XX_CLK_DIV1) & S5PC100_CLKDIV1_MPLL2_MASK;
+ rate /= (clkdiv >> S5PC100_CLKDIV1_MPLL2_SHIFT) + 1;
+
+ return rate;
+}
+
+struct clk clk_dout_mpll2 = {
+ .name = "dout_mpll2",
+ .id = -1,
+ .parent = &clk_mout_mpll.clk,
+ .get_rate = s5pc1xx_clk_doutmpll2_get_rate,
+};
+
+static struct clk *clkset_uart_list[] = {
+ &clk_mout_epll.clk,
+ &clk_dout_mpll,
+ NULL,
+ NULL
+};
+
+static struct clk_sources clkset_uart = {
+ .sources = clkset_uart_list,
+ .nr_sources = ARRAY_SIZE(clkset_uart_list),
+};
+
+static inline struct clksrc_clk *to_clksrc(struct clk *clk)
+{
+ return container_of(clk, struct clksrc_clk, clk);
+}
+
+static unsigned long s5pc1xx_getrate_clksrc(struct clk *clk)
+{
+ struct clksrc_clk *sclk = to_clksrc(clk);
+ unsigned long rate = clk_get_rate(clk->parent);
+ u32 clkdiv = __raw_readl(sclk->reg_divider);
+
+ clkdiv >>= sclk->divider_shift;
+ clkdiv &= 0xf;
+ clkdiv++;
+
+ rate /= clkdiv;
+ return rate;
+}
+
+static int s5pc1xx_setrate_clksrc(struct clk *clk, unsigned long rate)
+{
+ struct clksrc_clk *sclk = to_clksrc(clk);
+ void __iomem *reg = sclk->reg_divider;
+ unsigned int div;
+ u32 val;
+
+ rate = clk_round_rate(clk, rate);
+ div = clk_get_rate(clk->parent) / rate;
+ if (div > 16)
+ return -EINVAL;
+
+ val = __raw_readl(reg);
+ val &= ~(0xf << sclk->shift);
+ val |= (div - 1) << sclk->shift;
+ __raw_writel(val, reg);
+
+ return 0;
+}
+
+static int s5pc1xx_setparent_clksrc(struct clk *clk, struct clk *parent)
+{
+ struct clksrc_clk *sclk = to_clksrc(clk);
+ struct clk_sources *srcs = sclk->sources;
+ u32 clksrc = __raw_readl(sclk->reg_source);
+ int src_nr = -1;
+ int ptr;
+
+ for (ptr = 0; ptr < srcs->nr_sources; ptr++)
+ if (srcs->sources[ptr] == parent) {
+ src_nr = ptr;
+ break;
+ }
+
+ if (src_nr >= 0) {
+ clksrc &= ~sclk->mask;
+ clksrc |= src_nr << sclk->shift;
+
+ __raw_writel(clksrc, sclk->reg_source);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static unsigned long s5pc1xx_roundrate_clksrc(struct clk *clk,
+ unsigned long rate)
+{
+ unsigned long parent_rate = clk_get_rate(clk->parent);
+ int div;
+
+ if (rate > parent_rate)
+ rate = parent_rate;
+ else {
+ div = rate / parent_rate;
+
+ if (div == 0)
+ div = 1;
+ if (div > 16)
+ div = 16;
+
+ rate = parent_rate / div;
+ }
+
+ return rate;
+}
+
+static struct clksrc_clk clk_uart_uclk1 = {
+ .clk = {
+ .name = "uclk1",
+ .id = -1,
+ .ctrlbit = S5PC100_CLKGATE_SCLK0_UART,
+ .enable = s5pc1xx_sclk0_ctrl,
+ .set_parent = s5pc1xx_setparent_clksrc,
+ .get_rate = s5pc1xx_getrate_clksrc,
+ .set_rate = s5pc1xx_setrate_clksrc,
+ .round_rate = s5pc1xx_roundrate_clksrc,
+ },
+ .shift = S5PC100_CLKSRC1_UART_SHIFT,
+ .mask = S5PC100_CLKSRC1_UART_MASK,
+ .sources = &clkset_uart,
+ .divider_shift = S5PC100_CLKDIV2_UART_SHIFT,
+ .reg_divider = S5PC1XX_CLK_DIV2,
+ .reg_source = S5PC1XX_CLK_SRC1,
+};
+
+/* Clock initialisation code */
+
+static struct clksrc_clk *init_parents[] = {
+ &clk_mout_apll,
+ &clk_mout_epll,
+ &clk_mout_mpll,
+ &clk_uart_uclk1,
+};
+
+static void __init_or_cpufreq s5pc1xx_set_clksrc(struct clksrc_clk *clk)
+{
+ struct clk_sources *srcs = clk->sources;
+ u32 clksrc = __raw_readl(clk->reg_source);
+
+ clksrc &= clk->mask;
+ clksrc >>= clk->shift;
+
+ if (clksrc > srcs->nr_sources || !srcs->sources[clksrc]) {
+ printk(KERN_ERR "%s: bad source %d\n",
+ clk->clk.name, clksrc);
+ return;
+ }
+
+ clk->clk.parent = srcs->sources[clksrc];
+
+ printk(KERN_INFO "%s: source is %s (%d), rate is %ld\n",
+ clk->clk.name, clk->clk.parent->name, clksrc,
+ clk_get_rate(&clk->clk));
+}
+
+#define GET_DIV(clk, field) ((((clk) & field##_MASK) >> field##_SHIFT) + 1)
+
+void __init_or_cpufreq s5pc100_setup_clocks(void)
+{
+ struct clk *xtal_clk;
+ unsigned long xtal;
+ unsigned long armclk;
+ unsigned long hclkd0;
+ unsigned long hclk;
+ unsigned long pclkd0;
+ unsigned long pclk;
+ unsigned long apll;
+ unsigned long mpll;
+ unsigned long hpll;
+ unsigned long epll;
+ unsigned int ptr;
+ u32 clkdiv0, clkdiv1;
+
+ printk(KERN_DEBUG "%s: registering clocks\n", __func__);
+
+ clkdiv0 = __raw_readl(S5PC1XX_CLK_DIV0);
+ clkdiv1 = __raw_readl(S5PC1XX_CLK_DIV1);
+
+ printk(KERN_DEBUG "%s: clkdiv0 = %08x, clkdiv1 = %08x\n",
+ __func__, clkdiv0, clkdiv1);
+
+ xtal_clk = clk_get(NULL, "xtal");
+ BUG_ON(IS_ERR(xtal_clk));
+
+ xtal = clk_get_rate(xtal_clk);
+ clk_put(xtal_clk);
+
+ printk(KERN_DEBUG "%s: xtal is %ld\n", __func__, xtal);
+
+ apll = s5pc1xx_get_pll(xtal, __raw_readl(S5PC1XX_APLL_CON));
+ mpll = s5pc1xx_get_pll(xtal, __raw_readl(S5PC1XX_MPLL_CON));
+ epll = s5pc1xx_get_pll(xtal, __raw_readl(S5PC1XX_EPLL_CON));
+ hpll = s5pc1xx_get_pll(xtal, __raw_readl(S5PC100_HPLL_CON));
+
+ printk(KERN_INFO "S5PC100: PLL settings, A=%ld, M=%ld, E=%ld, H=%ld\n",
+ apll, mpll, epll, hpll);
+
+ armclk = apll / GET_DIV(clkdiv0, S5PC1XX_CLKDIV0_APLL);
+ armclk = armclk / GET_DIV(clkdiv0, S5PC100_CLKDIV0_ARM);
+ hclkd0 = armclk / GET_DIV(clkdiv0, S5PC100_CLKDIV0_D0);
+ pclkd0 = hclkd0 / GET_DIV(clkdiv0, S5PC100_CLKDIV0_PCLKD0);
+ hclk = mpll / GET_DIV(clkdiv1, S5PC100_CLKDIV1_D1);
+ pclk = hclk / GET_DIV(clkdiv1, S5PC100_CLKDIV1_PCLKD1);
+
+ printk(KERN_INFO "S5PC100: ARMCLK=%ld, HCLKD0=%ld, PCLKD0=%ld, HCLK=%ld, PCLK=%ld\n",
+ armclk, hclkd0, pclkd0, hclk, pclk);
+
+ clk_fout_apll.rate = apll;
+ clk_fout_mpll.rate = mpll;
+ clk_fout_epll.rate = epll;
+ clk_fout_apll.rate = apll;
+
+ clk_h.rate = hclk;
+ clk_p.rate = pclk;
+
+ for (ptr = 0; ptr < ARRAY_SIZE(init_parents); ptr++)
+ s5pc1xx_set_clksrc(init_parents[ptr]);
+}
+
+static struct clk *clks[] __initdata = {
+ &clk_ext_xtal_mux,
+ &clk_mout_epll.clk,
+ &clk_fout_epll,
+ &clk_mout_mpll.clk,
+ &clk_dout_mpll,
+ &clk_uart_uclk1.clk,
+ &clk_ext,
+ &clk_epll,
+ &clk_27m,
+ &clk_48m,
+ &clk_54m,
+};
+
+void __init s5pc100_register_clocks(void)
+{
+ struct clk *clkp;
+ int ret;
+ int ptr;
+
+ for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) {
+ clkp = clks[ptr];
+ ret = s3c24xx_register_clock(clkp);
+ if (ret < 0) {
+ printk(KERN_ERR "Failed to register clock %s (%d)\n",
+ clkp->name, ret);
+ }
+ }
+
+ clk_mpll.parent = &clk_mout_mpll.clk;
+ clk_epll.parent = &clk_mout_epll.clk;
+}
diff --git a/arch/arm/plat-s5pc1xx/s5pc100-init.c b/arch/arm/plat-s5pc1xx/s5pc100-init.c
new file mode 100644
index 00000000000..c58710884ce
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/s5pc100-init.c
@@ -0,0 +1,27 @@
+/* linux/arch/arm/plat-s5pc1xx/s5pc100-init.c
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * S5PC100 - CPU initialisation (common with other S5PC1XX chips)
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+
+#include <plat/cpu.h>
+#include <plat/devs.h>
+#include <plat/s5pc100.h>
+
+/* uart registration process */
+
+void __init s5pc100_common_init_uarts(struct s3c2410_uartcfg *cfg, int no)
+{
+ /* The driver name is s3c6400-uart to reuse s3c6400_serial_drv */
+ s3c24xx_init_uartdevs("s3c6400-uart", s5pc1xx_uart_resources, cfg, no);
+}
diff --git a/arch/arm/plat-s5pc1xx/setup-i2c0.c b/arch/arm/plat-s5pc1xx/setup-i2c0.c
new file mode 100644
index 00000000000..3d00c025fff
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/setup-i2c0.c
@@ -0,0 +1,25 @@
+/* linux/arch/arm/plat-s5pc1xx/setup-i2c0.c
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * Base S5PC1XX I2C bus 0 gpio configuration
+ *
+ * Based on plat-s3c64xx/setup-i2c0.c
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+
+struct platform_device; /* don't need the contents */
+
+#include <plat/iic.h>
+
+void s3c_i2c0_cfg_gpio(struct platform_device *dev)
+{
+ /* Pin configuration would be needed */
+}
diff --git a/arch/arm/plat-s5pc1xx/setup-i2c1.c b/arch/arm/plat-s5pc1xx/setup-i2c1.c
new file mode 100644
index 00000000000..c8f3ca42f51
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/setup-i2c1.c
@@ -0,0 +1,25 @@
+/* linux/arch/arm/plat-s3c64xx/setup-i2c1.c
+ *
+ * Copyright 2009 Samsung Electronics Co.
+ * Byungho Min <bhmin@samsung.com>
+ *
+ * Base S5PC1XX I2C bus 1 gpio configuration
+ *
+ * Based on plat-s3c64xx/setup-i2c1.c
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+
+struct platform_device; /* don't need the contents */
+
+#include <plat/iic.h>
+
+void s3c_i2c1_cfg_gpio(struct platform_device *dev)
+{
+ /* Pin configuration would be needed */
+}
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index 33026eff2aa..c8c55b46934 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -12,7 +12,7 @@
#
# http://www.arm.linux.org.uk/developer/machines/?action=new
#
-# Last update: Sat Jun 20 22:28:39 2009
+# Last update: Sat Sep 12 12:00:16 2009
#
# machine_is_xxx CONFIG_xxxx MACH_TYPE_xxx number
#
@@ -1769,7 +1769,7 @@ mx31cicada MACH_MX31CICADA MX31CICADA 1777
mi424wr MACH_MI424WR MI424WR 1778
axs_ultrax MACH_AXS_ULTRAX AXS_ULTRAX 1779
at572d940deb MACH_AT572D940DEB AT572D940DEB 1780
-davinci_da8xx_evm MACH_DAVINCI_DA8XX_EVM DAVINCI_DA8XX_EVM 1781
+davinci_da830_evm MACH_DAVINCI_DA830_EVM DAVINCI_DA830_EVM 1781
ep9302 MACH_EP9302 EP9302 1782
at572d940hfek MACH_AT572D940HFEB AT572D940HFEB 1783
cybook3 MACH_CYBOOK3 CYBOOK3 1784
@@ -1962,7 +1962,7 @@ ethernut5 MACH_ETHERNUT5 ETHERNUT5 1971
arm11 MACH_ARM11 ARM11 1972
cpuat9260 MACH_CPUAT9260 CPUAT9260 1973
cpupxa255 MACH_CPUPXA255 CPUPXA255 1974
-cpuimx27 MACH_CPUIMX27 CPUIMX27 1975
+eukrea_cpuimx27 MACH_CPUIMX27 CPUIMX27 1975
cheflux MACH_CHEFLUX CHEFLUX 1976
eb_cpux9k2 MACH_EB_CPUX9K2 EB_CPUX9K2 1977
opcotec MACH_OPCOTEC OPCOTEC 1978
@@ -2249,14 +2249,14 @@ omap3_phrazer MACH_OMAP3_PHRAZER OMAP3_PHRAZER 2261
darwin MACH_DARWIN DARWIN 2262
oratiscomu MACH_ORATISCOMU ORATISCOMU 2263
rtsbc20 MACH_RTSBC20 RTSBC20 2264
-i780 MACH_I780 I780 2265
+sgh_i780 MACH_I780 I780 2265
gemini324 MACH_GEMINI324 GEMINI324 2266
oratislan MACH_ORATISLAN ORATISLAN 2267
oratisalog MACH_ORATISALOG ORATISALOG 2268
oratismadi MACH_ORATISMADI ORATISMADI 2269
oratisot16 MACH_ORATISOT16 ORATISOT16 2270
oratisdesk MACH_ORATISDESK ORATISDESK 2271
-v2p_ca9 MACH_V2P_CA9 V2P_CA9 2272
+v2_ca9 MACH_V2P_CA9 V2P_CA9 2272
sintexo MACH_SINTEXO SINTEXO 2273
cm3389 MACH_CM3389 CM3389 2274
omap3_cio MACH_OMAP3_CIO OMAP3_CIO 2275
@@ -2280,3 +2280,132 @@ htcrhodium MACH_HTCRHODIUM HTCRHODIUM 2292
htctopaz MACH_HTCTOPAZ HTCTOPAZ 2293
matrix504 MACH_MATRIX504 MATRIX504 2294
mrfsa MACH_MRFSA MRFSA 2295
+sc_p270 MACH_SC_P270 SC_P270 2296
+atlas5_evb MACH_ATLAS5_EVB ATLAS5_EVB 2297
+pelco_lobox MACH_PELCO_LOBOX PELCO_LOBOX 2298
+dilax_pcu200 MACH_DILAX_PCU200 DILAX_PCU200 2299
+leonardo MACH_LEONARDO LEONARDO 2300
+zoran_approach7 MACH_ZORAN_APPROACH7 ZORAN_APPROACH7 2301
+dp6xx MACH_DP6XX DP6XX 2302
+bcm2153_vesper MACH_BCM2153_VESPER BCM2153_VESPER 2303
+mahimahi MACH_MAHIMAHI MAHIMAHI 2304
+clickc MACH_CLICKC CLICKC 2305
+zb_gateway MACH_ZB_GATEWAY ZB_GATEWAY 2306
+tazcard MACH_TAZCARD TAZCARD 2307
+tazdev MACH_TAZDEV TAZDEV 2308
+annax_cb_arm MACH_ANNAX_CB_ARM ANNAX_CB_ARM 2309
+annax_dm3 MACH_ANNAX_DM3 ANNAX_DM3 2310
+cerebric MACH_CEREBRIC CEREBRIC 2311
+orca MACH_ORCA ORCA 2312
+pc9260 MACH_PC9260 PC9260 2313
+ems285a MACH_EMS285A EMS285A 2314
+gec2410 MACH_GEC2410 GEC2410 2315
+gec2440 MACH_GEC2440 GEC2440 2316
+mw903 MACH_ARCH_MW903 ARCH_MW903 2317
+mw2440 MACH_MW2440 MW2440 2318
+ecac2378 MACH_ECAC2378 ECAC2378 2319
+tazkiosk MACH_TAZKIOSK TAZKIOSK 2320
+whiterabbit_mch MACH_WHITERABBIT_MCH WHITERABBIT_MCH 2321
+sbox9263 MACH_SBOX9263 SBOX9263 2322
+oreo MACH_OREO OREO 2323
+smdk6442 MACH_SMDK6442 SMDK6442 2324
+openrd_base MACH_OPENRD_BASE OPENRD_BASE 2325
+incredible MACH_INCREDIBLE INCREDIBLE 2326
+incrediblec MACH_INCREDIBLEC INCREDIBLEC 2327
+heroct MACH_HEROCT HEROCT 2328
+mmnet1000 MACH_MMNET1000 MMNET1000 2329
+devkit8000 MACH_DEVKIT8000 DEVKIT8000 2330
+devkit9000 MACH_DEVKIT9000 DEVKIT9000 2331
+mx31txtr MACH_MX31TXTR MX31TXTR 2332
+u380 MACH_U380 U380 2333
+oamp3_hualu MACH_HUALU_BOARD HUALU_BOARD 2334
+npcmx50 MACH_NPCMX50 NPCMX50 2335
+mx51_lange51 MACH_MX51_LANGE51 MX51_LANGE51 2336
+mx51_lange52 MACH_MX51_LANGE52 MX51_LANGE52 2337
+riom MACH_RIOM RIOM 2338
+comcas MACH_COMCAS COMCAS 2339
+wsi_mx27 MACH_WSI_MX27 WSI_MX27 2340
+cm_t35 MACH_CM_T35 CM_T35 2341
+net2big MACH_NET2BIG NET2BIG 2342
+motorola_a1600 MACH_MOTOROLA_A1600 MOTOROLA_A1600 2343
+igep0020 MACH_IGEP0020 IGEP0020 2344
+igep0010 MACH_IGEP0010 IGEP0010 2345
+mv6281gtwge2 MACH_MV6281GTWGE2 MV6281GTWGE2 2346
+scat100 MACH_SCAT100 SCAT100 2347
+sanmina MACH_SANMINA SANMINA 2348
+momento MACH_MOMENTO MOMENTO 2349
+nuc9xx MACH_NUC9XX NUC9XX 2350
+nuc910evb MACH_NUC910EVB NUC910EVB 2351
+nuc920evb MACH_NUC920EVB NUC920EVB 2352
+nuc950evb MACH_NUC950EVB NUC950EVB 2353
+nuc945evb MACH_NUC945EVB NUC945EVB 2354
+nuc960evb MACH_NUC960EVB NUC960EVB 2355
+nuc932evb MACH_NUC932EVB NUC932EVB 2356
+nuc900 MACH_NUC900 NUC900 2357
+sd1soc MACH_SD1SOC SD1SOC 2358
+ln2440bc MACH_LN2440BC LN2440BC 2359
+rsbc MACH_RSBC RSBC 2360
+openrd_client MACH_OPENRD_CLIENT OPENRD_CLIENT 2361
+hpipaq11x MACH_HPIPAQ11X HPIPAQ11X 2362
+wayland MACH_WAYLAND WAYLAND 2363
+acnbsx102 MACH_ACNBSX102 ACNBSX102 2364
+hwat91 MACH_HWAT91 HWAT91 2365
+at91sam9263cs MACH_AT91SAM9263CS AT91SAM9263CS 2366
+csb732 MACH_CSB732 CSB732 2367
+u8500 MACH_U8500 U8500 2368
+huqiu MACH_HUQIU HUQIU 2369
+mx51_kunlun MACH_MX51_KUNLUN MX51_KUNLUN 2370
+pmt1g MACH_PMT1G PMT1G 2371
+htcelf MACH_HTCELF HTCELF 2372
+armadillo420 MACH_ARMADILLO420 ARMADILLO420 2373
+armadillo440 MACH_ARMADILLO440 ARMADILLO440 2374
+u_chip_dual_arm MACH_U_CHIP_DUAL_ARM U_CHIP_DUAL_ARM 2375
+csr_bdb3 MACH_CSR_BDB3 CSR_BDB3 2376
+dolby_cat1018 MACH_DOLBY_CAT1018 DOLBY_CAT1018 2377
+hy9307 MACH_HY9307 HY9307 2378
+aspire_easystore MACH_A_ES A_ES 2379
+davinci_irif MACH_DAVINCI_IRIF DAVINCI_IRIF 2380
+agama9263 MACH_AGAMA9263 AGAMA9263 2381
+marvell_jasper MACH_MARVELL_JASPER MARVELL_JASPER 2382
+flint MACH_FLINT FLINT 2383
+tavorevb3 MACH_TAVOREVB3 TAVOREVB3 2384
+sch_m490 MACH_SCH_M490 SCH_M490 2386
+rbl01 MACH_RBL01 RBL01 2387
+omnifi MACH_OMNIFI OMNIFI 2388
+otavalo MACH_OTAVALO OTAVALO 2389
+sienna MACH_SIENNA SIENNA 2390
+htc_excalibur_s620 MACH_HTC_EXCALIBUR_S620 HTC_EXCALIBUR_S620 2391
+htc_opal MACH_HTC_OPAL HTC_OPAL 2392
+touchbook MACH_TOUCHBOOK TOUCHBOOK 2393
+latte MACH_LATTE LATTE 2394
+xa200 MACH_XA200 XA200 2395
+nimrod MACH_NIMROD NIMROD 2396
+cc9p9215_3g MACH_CC9P9215_3G CC9P9215_3G 2397
+cc9p9215_3gjs MACH_CC9P9215_3GJS CC9P9215_3GJS 2398
+tk71 MACH_TK71 TK71 2399
+comham3525 MACH_COMHAM3525 COMHAM3525 2400
+mx31erebus MACH_MX31EREBUS MX31EREBUS 2401
+mcardmx27 MACH_MCARDMX27 MCARDMX27 2402
+paradise MACH_PARADISE PARADISE 2403
+tide MACH_TIDE TIDE 2404
+wzl2440 MACH_WZL2440 WZL2440 2405
+sdrdemo MACH_SDRDEMO SDRDEMO 2406
+ethercan2 MACH_ETHERCAN2 ETHERCAN2 2407
+ecmimg20 MACH_ECMIMG20 ECMIMG20 2408
+omap_dragon MACH_OMAP_DRAGON OMAP_DRAGON 2409
+halo MACH_HALO HALO 2410
+huangshan MACH_HUANGSHAN HUANGSHAN 2411
+vl_ma2sc MACH_VL_MA2SC VL_MA2SC 2412
+raumfeld_rc MACH_RAUMFELD_RC RAUMFELD_RC 2413
+raumfeld_connector MACH_RAUMFELD_CONNECTOR RAUMFELD_CONNECTOR 2414
+raumfeld_speaker MACH_RAUMFELD_SPEAKER RAUMFELD_SPEAKER 2415
+multibus_master MACH_MULTIBUS_MASTER MULTIBUS_MASTER 2416
+multibus_pbk MACH_MULTIBUS_PBK MULTIBUS_PBK 2417
+tnetv107x MACH_TNETV107X TNETV107X 2418
+snake MACH_SNAKE SNAKE 2419
+cwmx27 MACH_CWMX27 CWMX27 2420
+sch_m480 MACH_SCH_M480 SCH_M480 2421
+platypus MACH_PLATYPUS PLATYPUS 2422
+pss2 MACH_PSS2 PSS2 2423
+davinci_apm150 MACH_DAVINCI_APM150 DAVINCI_APM150 2424
+str9100 MACH_STR9100 STR9100 2425
diff --git a/arch/arm/vfp/entry.S b/arch/arm/vfp/entry.S
index a2bed62aec2..4fa9903b83c 100644
--- a/arch/arm/vfp/entry.S
+++ b/arch/arm/vfp/entry.S
@@ -42,6 +42,7 @@ ENTRY(vfp_null_entry)
mov pc, lr
ENDPROC(vfp_null_entry)
+ .align 2
.LCvfp:
.word vfp_vector
@@ -61,6 +62,7 @@ ENTRY(vfp_testing_entry)
mov pc, r9 @ we have handled the fault
ENDPROC(vfp_testing_entry)
+ .align 2
VFP_arch_address:
.word VFP_arch
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S
index 1aeae38725d..66dc2d03b7f 100644
--- a/arch/arm/vfp/vfphw.S
+++ b/arch/arm/vfp/vfphw.S
@@ -209,40 +209,55 @@ ENDPROC(vfp_save_state)
last_VFP_context_address:
.word last_VFP_context
-ENTRY(vfp_get_float)
- add pc, pc, r0, lsl #3
+ .macro tbl_branch, base, tmp, shift
+#ifdef CONFIG_THUMB2_KERNEL
+ adr \tmp, 1f
+ add \tmp, \tmp, \base, lsl \shift
+ mov pc, \tmp
+#else
+ add pc, pc, \base, lsl \shift
mov r0, r0
+#endif
+1:
+ .endm
+
+ENTRY(vfp_get_float)
+ tbl_branch r0, r3, #3
.irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
- mrc p10, 0, r0, c\dr, c0, 0 @ fmrs r0, s0
+1: mrc p10, 0, r0, c\dr, c0, 0 @ fmrs r0, s0
mov pc, lr
- mrc p10, 0, r0, c\dr, c0, 4 @ fmrs r0, s1
+ .org 1b + 8
+1: mrc p10, 0, r0, c\dr, c0, 4 @ fmrs r0, s1
mov pc, lr
+ .org 1b + 8
.endr
ENDPROC(vfp_get_float)
ENTRY(vfp_put_float)
- add pc, pc, r1, lsl #3
- mov r0, r0
+ tbl_branch r1, r3, #3
.irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
- mcr p10, 0, r0, c\dr, c0, 0 @ fmsr r0, s0
+1: mcr p10, 0, r0, c\dr, c0, 0 @ fmsr r0, s0
mov pc, lr
- mcr p10, 0, r0, c\dr, c0, 4 @ fmsr r0, s1
+ .org 1b + 8
+1: mcr p10, 0, r0, c\dr, c0, 4 @ fmsr r0, s1
mov pc, lr
+ .org 1b + 8
.endr
ENDPROC(vfp_put_float)
ENTRY(vfp_get_double)
- add pc, pc, r0, lsl #3
- mov r0, r0
+ tbl_branch r0, r3, #3
.irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
- fmrrd r0, r1, d\dr
+1: fmrrd r0, r1, d\dr
mov pc, lr
+ .org 1b + 8
.endr
#ifdef CONFIG_VFPv3
@ d16 - d31 registers
.irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
- mrrc p11, 3, r0, r1, c\dr @ fmrrd r0, r1, d\dr
+1: mrrc p11, 3, r0, r1, c\dr @ fmrrd r0, r1, d\dr
mov pc, lr
+ .org 1b + 8
.endr
#endif
@@ -253,17 +268,18 @@ ENTRY(vfp_get_double)
ENDPROC(vfp_get_double)
ENTRY(vfp_put_double)
- add pc, pc, r2, lsl #3
- mov r0, r0
+ tbl_branch r2, r3, #3
.irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
- fmdrr d\dr, r0, r1
+1: fmdrr d\dr, r0, r1
mov pc, lr
+ .org 1b + 8
.endr
#ifdef CONFIG_VFPv3
@ d16 - d31 registers
.irp dr,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
- mcrr p11, 3, r1, r2, c\dr @ fmdrr r1, r2, d\dr
+1: mcrr p11, 3, r1, r2, c\dr @ fmdrr r1, r2, d\dr
mov pc, lr
+ .org 1b + 8
.endr
#endif
ENDPROC(vfp_put_double)
diff --git a/arch/blackfin/include/asm/bfin_rotary.h b/arch/blackfin/include/asm/bfin_rotary.h
new file mode 100644
index 00000000000..425ece64fd5
--- /dev/null
+++ b/arch/blackfin/include/asm/bfin_rotary.h
@@ -0,0 +1,39 @@
+/*
+ * board initialization should put one of these structures into platform_data
+ * and place the bfin-rotary onto platform_bus named "bfin-rotary".
+ */
+
+#ifndef _BFIN_ROTARY_H
+#define _BFIN_ROTARY_H
+
+/* mode bitmasks */
+#define ROT_QUAD_ENC CNTMODE_QUADENC /* quadrature/grey code encoder mode */
+#define ROT_BIN_ENC CNTMODE_BINENC /* binary encoder mode */
+#define ROT_UD_CNT CNTMODE_UDCNT /* rotary counter mode */
+#define ROT_DIR_CNT CNTMODE_DIRCNT /* direction counter mode */
+
+#define ROT_DEBE DEBE /* Debounce Enable */
+
+#define ROT_CDGINV CDGINV /* CDG Pin Polarity Invert */
+#define ROT_CUDINV CUDINV /* CUD Pin Polarity Invert */
+#define ROT_CZMINV CZMINV /* CZM Pin Polarity Invert */
+
+struct bfin_rotary_platform_data {
+ /* set rotary UP KEY_### or BTN_### in case you prefer
+ * bfin-rotary to send EV_KEY otherwise set 0
+ */
+ unsigned int rotary_up_key;
+ /* set rotary DOWN KEY_### or BTN_### in case you prefer
+ * bfin-rotary to send EV_KEY otherwise set 0
+ */
+ unsigned int rotary_down_key;
+ /* set rotary BUTTON KEY_### or BTN_### */
+ unsigned int rotary_button_key;
+ /* set rotary Relative Axis REL_### in case you prefer
+ * bfin-rotary to send EV_REL otherwise set 0
+ */
+ unsigned int rotary_rel_code;
+ unsigned short debounce; /* 0..17 */
+ unsigned short mode;
+};
+#endif
diff --git a/arch/ia64/include/asm/kvm_host.h b/arch/ia64/include/asm/kvm_host.h
index 5f43697aed3..d9b6325a932 100644
--- a/arch/ia64/include/asm/kvm_host.h
+++ b/arch/ia64/include/asm/kvm_host.h
@@ -235,7 +235,8 @@ struct kvm_vm_data {
#define KVM_REQ_PTC_G 32
#define KVM_REQ_RESUME 33
-#define KVM_PAGES_PER_HPAGE 1
+#define KVM_NR_PAGE_SIZES 1
+#define KVM_PAGES_PER_HPAGE(x) 1
struct kvm;
struct kvm_vcpu;
@@ -465,7 +466,6 @@ struct kvm_arch {
unsigned long metaphysical_rr4;
unsigned long vmm_init_rr;
- int online_vcpus;
int is_sn2;
struct kvm_ioapic *vioapic;
diff --git a/arch/ia64/include/asm/kvm_para.h b/arch/ia64/include/asm/kvm_para.h
index 0d6d8ca07b8..1588aee781a 100644
--- a/arch/ia64/include/asm/kvm_para.h
+++ b/arch/ia64/include/asm/kvm_para.h
@@ -19,9 +19,13 @@
*
*/
+#ifdef __KERNEL__
+
static inline unsigned int kvm_arch_para_features(void)
{
return 0;
}
#endif
+
+#endif
diff --git a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S
index 23f846de62d..e6c5c3d5e1f 100644
--- a/arch/ia64/kernel/head.S
+++ b/arch/ia64/kernel/head.S
@@ -34,6 +34,7 @@
#include <asm/mca_asm.h>
#include <linux/init.h>
#include <linux/linkage.h>
+#include "head.h"
#ifdef CONFIG_HOTPLUG_CPU
#define SAL_PSR_BITS_TO_SET \
diff --git a/arch/ia64/kernel/head.h b/arch/ia64/kernel/head.h
new file mode 100644
index 00000000000..2e2ac6824e6
--- /dev/null
+++ b/arch/ia64/kernel/head.h
@@ -0,0 +1 @@
+extern void console_print(const char *s);
diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c
index 89969e95004..9bcec9945c1 100644
--- a/arch/ia64/kernel/process.c
+++ b/arch/ia64/kernel/process.c
@@ -161,6 +161,13 @@ show_regs (struct pt_regs *regs)
show_stack(NULL, NULL);
}
+/* local support for deprecated console_print */
+void
+console_print(const char *s)
+{
+ printk(KERN_EMERG "%s", s);
+}
+
void
do_notify_resume_user(sigset_t *unused, struct sigscratch *scr, long in_syscall)
{
diff --git a/arch/ia64/kvm/Kconfig b/arch/ia64/kvm/Kconfig
index 64d52093787..ef3e7be29ca 100644
--- a/arch/ia64/kvm/Kconfig
+++ b/arch/ia64/kvm/Kconfig
@@ -1,12 +1,8 @@
#
# KVM configuration
#
-config HAVE_KVM
- bool
-config HAVE_KVM_IRQCHIP
- bool
- default y
+source "virt/kvm/Kconfig"
menuconfig VIRTUALIZATION
bool "Virtualization"
@@ -28,6 +24,8 @@ config KVM
depends on PCI
select PREEMPT_NOTIFIERS
select ANON_INODES
+ select HAVE_KVM_IRQCHIP
+ select KVM_APIC_ARCHITECTURE
---help---
Support hosting fully virtualized guest machines using hardware
virtualization extensions. You will need a fairly recent
@@ -49,9 +47,6 @@ config KVM_INTEL
Provides support for KVM on Itanium 2 processors equipped with the VT
extensions.
-config KVM_TRACE
- bool
-
source drivers/virtio/Kconfig
endif # VIRTUALIZATION
diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c
index 80c57b0a21c..0ad09f05efa 100644
--- a/arch/ia64/kvm/kvm-ia64.c
+++ b/arch/ia64/kvm/kvm-ia64.c
@@ -210,16 +210,6 @@ int kvm_dev_ioctl_check_extension(long ext)
}
-static struct kvm_io_device *vcpu_find_mmio_dev(struct kvm_vcpu *vcpu,
- gpa_t addr, int len, int is_write)
-{
- struct kvm_io_device *dev;
-
- dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr, len, is_write);
-
- return dev;
-}
-
static int handle_vm_error(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
@@ -231,6 +221,7 @@ static int handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
struct kvm_mmio_req *p;
struct kvm_io_device *mmio_dev;
+ int r;
p = kvm_get_vcpu_ioreq(vcpu);
@@ -247,16 +238,13 @@ static int handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
kvm_run->exit_reason = KVM_EXIT_MMIO;
return 0;
mmio:
- mmio_dev = vcpu_find_mmio_dev(vcpu, p->addr, p->size, !p->dir);
- if (mmio_dev) {
- if (!p->dir)
- kvm_iodevice_write(mmio_dev, p->addr, p->size,
- &p->data);
- else
- kvm_iodevice_read(mmio_dev, p->addr, p->size,
- &p->data);
-
- } else
+ if (p->dir)
+ r = kvm_io_bus_read(&vcpu->kvm->mmio_bus, p->addr,
+ p->size, &p->data);
+ else
+ r = kvm_io_bus_write(&vcpu->kvm->mmio_bus, p->addr,
+ p->size, &p->data);
+ if (r)
printk(KERN_ERR"kvm: No iodevice found! addr:%lx\n", p->addr);
p->state = STATE_IORESP_READY;
@@ -337,13 +325,12 @@ static struct kvm_vcpu *lid_to_vcpu(struct kvm *kvm, unsigned long id,
{
union ia64_lid lid;
int i;
+ struct kvm_vcpu *vcpu;
- for (i = 0; i < kvm->arch.online_vcpus; i++) {
- if (kvm->vcpus[i]) {
- lid.val = VCPU_LID(kvm->vcpus[i]);
- if (lid.id == id && lid.eid == eid)
- return kvm->vcpus[i];
- }
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ lid.val = VCPU_LID(vcpu);
+ if (lid.id == id && lid.eid == eid)
+ return vcpu;
}
return NULL;
@@ -409,21 +396,21 @@ static int handle_global_purge(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
struct kvm *kvm = vcpu->kvm;
struct call_data call_data;
int i;
+ struct kvm_vcpu *vcpui;
call_data.ptc_g_data = p->u.ptc_g_data;
- for (i = 0; i < kvm->arch.online_vcpus; i++) {
- if (!kvm->vcpus[i] || kvm->vcpus[i]->arch.mp_state ==
- KVM_MP_STATE_UNINITIALIZED ||
- vcpu == kvm->vcpus[i])
+ kvm_for_each_vcpu(i, vcpui, kvm) {
+ if (vcpui->arch.mp_state == KVM_MP_STATE_UNINITIALIZED ||
+ vcpu == vcpui)
continue;
- if (waitqueue_active(&kvm->vcpus[i]->wq))
- wake_up_interruptible(&kvm->vcpus[i]->wq);
+ if (waitqueue_active(&vcpui->wq))
+ wake_up_interruptible(&vcpui->wq);
- if (kvm->vcpus[i]->cpu != -1) {
- call_data.vcpu = kvm->vcpus[i];
- smp_call_function_single(kvm->vcpus[i]->cpu,
+ if (vcpui->cpu != -1) {
+ call_data.vcpu = vcpui;
+ smp_call_function_single(vcpui->cpu,
vcpu_global_purge, &call_data, 1);
} else
printk(KERN_WARNING"kvm: Uninit vcpu received ipi!\n");
@@ -852,8 +839,6 @@ struct kvm *kvm_arch_create_vm(void)
kvm_init_vm(kvm);
- kvm->arch.online_vcpus = 0;
-
return kvm;
}
@@ -1000,10 +985,10 @@ long kvm_arch_vm_ioctl(struct file *filp,
goto out;
if (irqchip_in_kernel(kvm)) {
__s32 status;
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->irq_lock);
status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
irq_event.irq, irq_event.level);
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->irq_lock);
if (ioctl == KVM_IRQ_LINE_STATUS) {
irq_event.status = status;
if (copy_to_user(argp, &irq_event,
@@ -1216,7 +1201,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
if (IS_ERR(vmm_vcpu))
return PTR_ERR(vmm_vcpu);
- if (vcpu->vcpu_id == 0) {
+ if (kvm_vcpu_is_bsp(vcpu)) {
vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
/*Set entry address for first run.*/
@@ -1224,7 +1209,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
/*Initialize itc offset for vcpus*/
itc_offset = 0UL - kvm_get_itc(vcpu);
- for (i = 0; i < kvm->arch.online_vcpus; i++) {
+ for (i = 0; i < KVM_MAX_VCPUS; i++) {
v = (struct kvm_vcpu *)((char *)vcpu +
sizeof(struct kvm_vcpu_data) * i);
v->arch.itc_offset = itc_offset;
@@ -1356,8 +1341,6 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
goto fail;
}
- kvm->arch.online_vcpus++;
-
return vcpu;
fail:
return ERR_PTR(r);
@@ -1952,19 +1935,6 @@ int kvm_highest_pending_irq(struct kvm_vcpu *vcpu)
return find_highest_bits((int *)&vpd->irr[0]);
}
-int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
-{
- if (kvm_highest_pending_irq(vcpu) != -1)
- return 1;
- return 0;
-}
-
-int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu)
-{
- /* do real check here */
- return 1;
-}
-
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
{
return vcpu->arch.timer_fired;
@@ -1977,7 +1947,8 @@ gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
{
- return vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE;
+ return (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE) ||
+ (kvm_highest_pending_irq(vcpu) != -1);
}
int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
diff --git a/arch/ia64/kvm/vcpu.c b/arch/ia64/kvm/vcpu.c
index cc406d064a0..dce75b70cdd 100644
--- a/arch/ia64/kvm/vcpu.c
+++ b/arch/ia64/kvm/vcpu.c
@@ -830,8 +830,8 @@ static void vcpu_set_itc(struct kvm_vcpu *vcpu, u64 val)
kvm = (struct kvm *)KVM_VM_BASE;
- if (vcpu->vcpu_id == 0) {
- for (i = 0; i < kvm->arch.online_vcpus; i++) {
+ if (kvm_vcpu_is_bsp(vcpu)) {
+ for (i = 0; i < atomic_read(&kvm->online_vcpus); i++) {
v = (struct kvm_vcpu *)((char *)vcpu +
sizeof(struct kvm_vcpu_data) * i);
VMX(v, itc_offset) = itc_offset;
diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h
index fddc3ed715f..c9c930ed11d 100644
--- a/arch/powerpc/include/asm/kvm_host.h
+++ b/arch/powerpc/include/asm/kvm_host.h
@@ -34,7 +34,8 @@
#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
/* We don't currently support large pages. */
-#define KVM_PAGES_PER_HPAGE (1UL << 31)
+#define KVM_NR_PAGE_SIZES 1
+#define KVM_PAGES_PER_HPAGE(x) (1UL<<31)
struct kvm;
struct kvm_run;
@@ -153,7 +154,6 @@ struct kvm_vcpu_arch {
u32 pid;
u32 swap_pid;
- u32 pvr;
u32 ccr0;
u32 ccr1;
u32 dbcr0;
diff --git a/arch/powerpc/kvm/44x.c b/arch/powerpc/kvm/44x.c
index 0cef809cec2..f4d1b55aa70 100644
--- a/arch/powerpc/kvm/44x.c
+++ b/arch/powerpc/kvm/44x.c
@@ -138,7 +138,7 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
kmem_cache_free(kvm_vcpu_cache, vcpu_44x);
}
-static int kvmppc_44x_init(void)
+static int __init kvmppc_44x_init(void)
{
int r;
@@ -149,7 +149,7 @@ static int kvmppc_44x_init(void)
return kvm_init(NULL, sizeof(struct kvmppc_vcpu_44x), THIS_MODULE);
}
-static void kvmppc_44x_exit(void)
+static void __exit kvmppc_44x_exit(void)
{
kvmppc_booke_exit();
}
diff --git a/arch/powerpc/kvm/44x_tlb.c b/arch/powerpc/kvm/44x_tlb.c
index 4a16f472cc1..ff3cb63b811 100644
--- a/arch/powerpc/kvm/44x_tlb.c
+++ b/arch/powerpc/kvm/44x_tlb.c
@@ -30,6 +30,7 @@
#include "timing.h"
#include "44x_tlb.h"
+#include "trace.h"
#ifndef PPC44x_TLBE_SIZE
#define PPC44x_TLBE_SIZE PPC44x_TLB_4K
@@ -263,7 +264,7 @@ static void kvmppc_44x_shadow_release(struct kvmppc_vcpu_44x *vcpu_44x,
/* XXX set tlb_44x_index to stlb_index? */
- KVMTRACE_1D(STLB_INVAL, &vcpu_44x->vcpu, stlb_index, handler);
+ trace_kvm_stlb_inval(stlb_index);
}
void kvmppc_mmu_destroy(struct kvm_vcpu *vcpu)
@@ -365,8 +366,8 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gpa_t gpaddr,
/* Insert shadow mapping into hardware TLB. */
kvmppc_44x_tlbe_set_modified(vcpu_44x, victim);
kvmppc_44x_tlbwe(victim, &stlbe);
- KVMTRACE_5D(STLB_WRITE, vcpu, victim, stlbe.tid, stlbe.word0, stlbe.word1,
- stlbe.word2, handler);
+ trace_kvm_stlb_write(victim, stlbe.tid, stlbe.word0, stlbe.word1,
+ stlbe.word2);
}
/* For a particular guest TLB entry, invalidate the corresponding host TLB
@@ -485,8 +486,8 @@ int kvmppc_44x_emul_tlbwe(struct kvm_vcpu *vcpu, u8 ra, u8 rs, u8 ws)
kvmppc_mmu_map(vcpu, eaddr, gpaddr, gtlb_index);
}
- KVMTRACE_5D(GTLB_WRITE, vcpu, gtlb_index, tlbe->tid, tlbe->word0,
- tlbe->word1, tlbe->word2, handler);
+ trace_kvm_gtlb_write(gtlb_index, tlbe->tid, tlbe->word0, tlbe->word1,
+ tlbe->word2);
kvmppc_set_exit_type(vcpu, EMULATED_TLBWE_EXITS);
return EMULATE_DONE;
diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig
index 5a152a52796..c2992684661 100644
--- a/arch/powerpc/kvm/Kconfig
+++ b/arch/powerpc/kvm/Kconfig
@@ -2,8 +2,7 @@
# KVM configuration
#
-config HAVE_KVM_IRQCHIP
- bool
+source "virt/kvm/Kconfig"
menuconfig VIRTUALIZATION
bool "Virtualization"
@@ -59,17 +58,6 @@ config KVM_E500
If unsure, say N.
-config KVM_TRACE
- bool "KVM trace support"
- depends on KVM && MARKERS && SYSFS
- select RELAY
- select DEBUG_FS
- default n
- ---help---
- This option allows reading a trace of kvm-related events through
- relayfs. Note the ABI is not considered stable and will be
- modified in future updates.
-
source drivers/virtio/Kconfig
endif # VIRTUALIZATION
diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile
index 459c7ee580f..37655fe19f2 100644
--- a/arch/powerpc/kvm/Makefile
+++ b/arch/powerpc/kvm/Makefile
@@ -8,7 +8,9 @@ EXTRA_CFLAGS += -Ivirt/kvm -Iarch/powerpc/kvm
common-objs-y = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
-common-objs-$(CONFIG_KVM_TRACE) += $(addprefix ../../../virt/kvm/, kvm_trace.o)
+CFLAGS_44x_tlb.o := -I.
+CFLAGS_e500_tlb.o := -I.
+CFLAGS_emulate.o := -I.
kvm-objs := $(common-objs-y) powerpc.o emulate.o
obj-$(CONFIG_KVM_EXIT_TIMING) += timing.o
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
index 642e4204cf2..e7bf4d02948 100644
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -520,7 +520,7 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu,
return kvmppc_core_vcpu_translate(vcpu, tr);
}
-int kvmppc_booke_init(void)
+int __init kvmppc_booke_init(void)
{
unsigned long ivor[16];
unsigned long max_ivor = 0;
diff --git a/arch/powerpc/kvm/e500.c b/arch/powerpc/kvm/e500.c
index d8067fd81cd..64949eef43f 100644
--- a/arch/powerpc/kvm/e500.c
+++ b/arch/powerpc/kvm/e500.c
@@ -60,9 +60,6 @@ int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu)
kvmppc_e500_tlb_setup(vcpu_e500);
- /* Use the same core vertion as host's */
- vcpu->arch.pvr = mfspr(SPRN_PVR);
-
return 0;
}
@@ -132,7 +129,7 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu)
kmem_cache_free(kvm_vcpu_cache, vcpu_e500);
}
-static int kvmppc_e500_init(void)
+static int __init kvmppc_e500_init(void)
{
int r, i;
unsigned long ivor[3];
@@ -160,7 +157,7 @@ static int kvmppc_e500_init(void)
return kvm_init(NULL, sizeof(struct kvmppc_vcpu_e500), THIS_MODULE);
}
-static void kvmppc_e500_exit(void)
+static void __init kvmppc_e500_exit(void)
{
kvmppc_booke_exit();
}
diff --git a/arch/powerpc/kvm/e500_emulate.c b/arch/powerpc/kvm/e500_emulate.c
index 3f760414b9f..be95b8d8e3b 100644
--- a/arch/powerpc/kvm/e500_emulate.c
+++ b/arch/powerpc/kvm/e500_emulate.c
@@ -180,6 +180,9 @@ int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt)
case SPRN_MMUCSR0:
vcpu->arch.gpr[rt] = 0; break;
+ case SPRN_MMUCFG:
+ vcpu->arch.gpr[rt] = mfspr(SPRN_MMUCFG); break;
+
/* extra exceptions */
case SPRN_IVOR32:
vcpu->arch.gpr[rt] = vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_UNAVAIL];
diff --git a/arch/powerpc/kvm/e500_tlb.c b/arch/powerpc/kvm/e500_tlb.c
index 0e773fc2d5e..fb1e1dc11ba 100644
--- a/arch/powerpc/kvm/e500_tlb.c
+++ b/arch/powerpc/kvm/e500_tlb.c
@@ -22,6 +22,7 @@
#include "../mm/mmu_decl.h"
#include "e500_tlb.h"
+#include "trace.h"
#define to_htlb1_esel(esel) (tlb1_entry_num - (esel) - 1)
@@ -224,9 +225,8 @@ static void kvmppc_e500_stlbe_invalidate(struct kvmppc_vcpu_e500 *vcpu_e500,
kvmppc_e500_shadow_release(vcpu_e500, tlbsel, esel);
stlbe->mas1 = 0;
- KVMTRACE_5D(STLB_INVAL, &vcpu_e500->vcpu, index_of(tlbsel, esel),
- stlbe->mas1, stlbe->mas2, stlbe->mas3, stlbe->mas7,
- handler);
+ trace_kvm_stlb_inval(index_of(tlbsel, esel), stlbe->mas1, stlbe->mas2,
+ stlbe->mas3, stlbe->mas7);
}
static void kvmppc_e500_tlb1_invalidate(struct kvmppc_vcpu_e500 *vcpu_e500,
@@ -269,7 +269,7 @@ static inline void kvmppc_e500_deliver_tlb_miss(struct kvm_vcpu *vcpu,
tlbsel = (vcpu_e500->mas4 >> 28) & 0x1;
victim = (tlbsel == 0) ? tlb0_get_next_victim(vcpu_e500) : 0;
pidsel = (vcpu_e500->mas4 >> 16) & 0xf;
- tsized = (vcpu_e500->mas4 >> 8) & 0xf;
+ tsized = (vcpu_e500->mas4 >> 7) & 0x1f;
vcpu_e500->mas0 = MAS0_TLBSEL(tlbsel) | MAS0_ESEL(victim)
| MAS0_NV(vcpu_e500->guest_tlb_nv[tlbsel]);
@@ -309,7 +309,7 @@ static inline void kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500,
vcpu_e500->shadow_pages[tlbsel][esel] = new_page;
/* Force TS=1 IPROT=0 TSIZE=4KB for all guest mappings. */
- stlbe->mas1 = MAS1_TSIZE(BOOKE_PAGESZ_4K)
+ stlbe->mas1 = MAS1_TSIZE(BOOK3E_PAGESZ_4K)
| MAS1_TID(get_tlb_tid(gtlbe)) | MAS1_TS | MAS1_VALID;
stlbe->mas2 = (gvaddr & MAS2_EPN)
| e500_shadow_mas2_attrib(gtlbe->mas2,
@@ -319,9 +319,8 @@ static inline void kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500,
vcpu_e500->vcpu.arch.msr & MSR_PR);
stlbe->mas7 = (hpaddr >> 32) & MAS7_RPN;
- KVMTRACE_5D(STLB_WRITE, &vcpu_e500->vcpu, index_of(tlbsel, esel),
- stlbe->mas1, stlbe->mas2, stlbe->mas3, stlbe->mas7,
- handler);
+ trace_kvm_stlb_write(index_of(tlbsel, esel), stlbe->mas1, stlbe->mas2,
+ stlbe->mas3, stlbe->mas7);
}
/* XXX only map the one-one case, for now use TLB0 */
@@ -535,9 +534,8 @@ int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *vcpu)
gtlbe->mas3 = vcpu_e500->mas3;
gtlbe->mas7 = vcpu_e500->mas7;
- KVMTRACE_5D(GTLB_WRITE, vcpu, vcpu_e500->mas0,
- gtlbe->mas1, gtlbe->mas2, gtlbe->mas3, gtlbe->mas7,
- handler);
+ trace_kvm_gtlb_write(vcpu_e500->mas0, gtlbe->mas1, gtlbe->mas2,
+ gtlbe->mas3, gtlbe->mas7);
/* Invalidate shadow mappings for the about-to-be-clobbered TLBE. */
if (tlbe_is_host_safe(vcpu, gtlbe)) {
@@ -545,7 +543,7 @@ int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *vcpu)
case 0:
/* TLB0 */
gtlbe->mas1 &= ~MAS1_TSIZE(~0);
- gtlbe->mas1 |= MAS1_TSIZE(BOOKE_PAGESZ_4K);
+ gtlbe->mas1 |= MAS1_TSIZE(BOOK3E_PAGESZ_4K);
stlbsel = 0;
sesel = kvmppc_e500_stlbe_map(vcpu_e500, 0, esel);
@@ -679,14 +677,14 @@ void kvmppc_e500_tlb_setup(struct kvmppc_vcpu_e500 *vcpu_e500)
/* Insert large initial mapping for guest. */
tlbe = &vcpu_e500->guest_tlb[1][0];
- tlbe->mas1 = MAS1_VALID | MAS1_TSIZE(BOOKE_PAGESZ_256M);
+ tlbe->mas1 = MAS1_VALID | MAS1_TSIZE(BOOK3E_PAGESZ_256M);
tlbe->mas2 = 0;
tlbe->mas3 = E500_TLB_SUPER_PERM_MASK;
tlbe->mas7 = 0;
/* 4K map for serial output. Used by kernel wrapper. */
tlbe = &vcpu_e500->guest_tlb[1][1];
- tlbe->mas1 = MAS1_VALID | MAS1_TSIZE(BOOKE_PAGESZ_4K);
+ tlbe->mas1 = MAS1_VALID | MAS1_TSIZE(BOOK3E_PAGESZ_4K);
tlbe->mas2 = (0xe0004500 & 0xFFFFF000) | MAS2_I | MAS2_G;
tlbe->mas3 = (0xe0004500 & 0xFFFFF000) | E500_TLB_SUPER_PERM_MASK;
tlbe->mas7 = 0;
diff --git a/arch/powerpc/kvm/e500_tlb.h b/arch/powerpc/kvm/e500_tlb.h
index 45b064b7690..d28e3010a5e 100644
--- a/arch/powerpc/kvm/e500_tlb.h
+++ b/arch/powerpc/kvm/e500_tlb.h
@@ -16,7 +16,7 @@
#define __KVM_E500_TLB_H__
#include <linux/kvm_host.h>
-#include <asm/mmu-fsl-booke.h>
+#include <asm/mmu-book3e.h>
#include <asm/tlb.h>
#include <asm/kvm_e500.h>
@@ -59,7 +59,7 @@ extern void kvmppc_e500_tlb_setup(struct kvmppc_vcpu_e500 *);
/* TLB helper functions */
static inline unsigned int get_tlb_size(const struct tlbe *tlbe)
{
- return (tlbe->mas1 >> 8) & 0xf;
+ return (tlbe->mas1 >> 7) & 0x1f;
}
static inline gva_t get_tlb_eaddr(const struct tlbe *tlbe)
@@ -70,7 +70,7 @@ static inline gva_t get_tlb_eaddr(const struct tlbe *tlbe)
static inline u64 get_tlb_bytes(const struct tlbe *tlbe)
{
unsigned int pgsize = get_tlb_size(tlbe);
- return 1ULL << 10 << (pgsize << 1);
+ return 1ULL << 10 << pgsize;
}
static inline gva_t get_tlb_end(const struct tlbe *tlbe)
diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c
index a561d6e8da1..7737146af3f 100644
--- a/arch/powerpc/kvm/emulate.c
+++ b/arch/powerpc/kvm/emulate.c
@@ -29,6 +29,7 @@
#include <asm/kvm_ppc.h>
#include <asm/disassemble.h>
#include "timing.h"
+#include "trace.h"
#define OP_TRAP 3
@@ -187,7 +188,9 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
case SPRN_SRR1:
vcpu->arch.gpr[rt] = vcpu->arch.srr1; break;
case SPRN_PVR:
- vcpu->arch.gpr[rt] = vcpu->arch.pvr; break;
+ vcpu->arch.gpr[rt] = mfspr(SPRN_PVR); break;
+ case SPRN_PIR:
+ vcpu->arch.gpr[rt] = mfspr(SPRN_PIR); break;
/* Note: mftb and TBRL/TBWL are user-accessible, so
* the guest can always access the real TB anyways.
@@ -417,7 +420,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
}
}
- KVMTRACE_3D(PPC_INSTR, vcpu, inst, (int)vcpu->arch.pc, emulated, entryexit);
+ trace_kvm_ppc_instr(inst, vcpu->arch.pc, emulated);
if (advance)
vcpu->arch.pc += 4; /* Advance past emulated instruction. */
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index 2cf915e51e7..2a4551f78f6 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -31,25 +31,17 @@
#include "timing.h"
#include "../mm/mmu_decl.h"
+#define CREATE_TRACE_POINTS
+#include "trace.h"
+
gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
{
return gfn;
}
-int kvm_cpu_has_interrupt(struct kvm_vcpu *v)
-{
- return !!(v->arch.pending_exceptions);
-}
-
-int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu)
-{
- /* do real check here */
- return 1;
-}
-
int kvm_arch_vcpu_runnable(struct kvm_vcpu *v)
{
- return !(v->arch.msr & MSR_WE);
+ return !(v->arch.msr & MSR_WE) || !!(v->arch.pending_exceptions);
}
@@ -122,13 +114,17 @@ struct kvm *kvm_arch_create_vm(void)
static void kvmppc_free_vcpus(struct kvm *kvm)
{
unsigned int i;
+ struct kvm_vcpu *vcpu;
- for (i = 0; i < KVM_MAX_VCPUS; ++i) {
- if (kvm->vcpus[i]) {
- kvm_arch_vcpu_free(kvm->vcpus[i]);
- kvm->vcpus[i] = NULL;
- }
- }
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ kvm_arch_vcpu_free(vcpu);
+
+ mutex_lock(&kvm->lock);
+ for (i = 0; i < atomic_read(&kvm->online_vcpus); i++)
+ kvm->vcpus[i] = NULL;
+
+ atomic_set(&kvm->online_vcpus, 0);
+ mutex_unlock(&kvm->lock);
}
void kvm_arch_sync_events(struct kvm *kvm)
diff --git a/arch/powerpc/kvm/trace.h b/arch/powerpc/kvm/trace.h
new file mode 100644
index 00000000000..67f219de045
--- /dev/null
+++ b/arch/powerpc/kvm/trace.h
@@ -0,0 +1,104 @@
+#if !defined(_TRACE_KVM_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_KVM_H
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM kvm
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE trace
+
+/*
+ * Tracepoint for guest mode entry.
+ */
+TRACE_EVENT(kvm_ppc_instr,
+ TP_PROTO(unsigned int inst, unsigned long pc, unsigned int emulate),
+ TP_ARGS(inst, pc, emulate),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, inst )
+ __field( unsigned long, pc )
+ __field( unsigned int, emulate )
+ ),
+
+ TP_fast_assign(
+ __entry->inst = inst;
+ __entry->pc = pc;
+ __entry->emulate = emulate;
+ ),
+
+ TP_printk("inst %u pc 0x%lx emulate %u\n",
+ __entry->inst, __entry->pc, __entry->emulate)
+);
+
+TRACE_EVENT(kvm_stlb_inval,
+ TP_PROTO(unsigned int stlb_index),
+ TP_ARGS(stlb_index),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, stlb_index )
+ ),
+
+ TP_fast_assign(
+ __entry->stlb_index = stlb_index;
+ ),
+
+ TP_printk("stlb_index %u", __entry->stlb_index)
+);
+
+TRACE_EVENT(kvm_stlb_write,
+ TP_PROTO(unsigned int victim, unsigned int tid, unsigned int word0,
+ unsigned int word1, unsigned int word2),
+ TP_ARGS(victim, tid, word0, word1, word2),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, victim )
+ __field( unsigned int, tid )
+ __field( unsigned int, word0 )
+ __field( unsigned int, word1 )
+ __field( unsigned int, word2 )
+ ),
+
+ TP_fast_assign(
+ __entry->victim = victim;
+ __entry->tid = tid;
+ __entry->word0 = word0;
+ __entry->word1 = word1;
+ __entry->word2 = word2;
+ ),
+
+ TP_printk("victim %u tid %u w0 %u w1 %u w2 %u",
+ __entry->victim, __entry->tid, __entry->word0,
+ __entry->word1, __entry->word2)
+);
+
+TRACE_EVENT(kvm_gtlb_write,
+ TP_PROTO(unsigned int gtlb_index, unsigned int tid, unsigned int word0,
+ unsigned int word1, unsigned int word2),
+ TP_ARGS(gtlb_index, tid, word0, word1, word2),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, gtlb_index )
+ __field( unsigned int, tid )
+ __field( unsigned int, word0 )
+ __field( unsigned int, word1 )
+ __field( unsigned int, word2 )
+ ),
+
+ TP_fast_assign(
+ __entry->gtlb_index = gtlb_index;
+ __entry->tid = tid;
+ __entry->word0 = word0;
+ __entry->word1 = word1;
+ __entry->word2 = word2;
+ ),
+
+ TP_printk("gtlb_index %u tid %u w0 %u w1 %u w2 %u",
+ __entry->gtlb_index, __entry->tid, __entry->word0,
+ __entry->word1, __entry->word2)
+);
+
+#endif /* _TRACE_KVM_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/arch/s390/include/asm/kvm.h b/arch/s390/include/asm/kvm.h
index 0b2f829f6d5..3dfcaeb5d7f 100644
--- a/arch/s390/include/asm/kvm.h
+++ b/arch/s390/include/asm/kvm.h
@@ -15,15 +15,6 @@
*/
#include <linux/types.h>
-/* for KVM_GET_IRQCHIP and KVM_SET_IRQCHIP */
-struct kvm_pic_state {
- /* no PIC for s390 */
-};
-
-struct kvm_ioapic_state {
- /* no IOAPIC for s390 */
-};
-
/* for KVM_GET_REGS and KVM_SET_REGS */
struct kvm_regs {
/* general purpose regs for s390 */
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index 698988f6940..27605b62b98 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -1,7 +1,7 @@
/*
* asm-s390/kvm_host.h - definition for kernel virtual machines on s390
*
- * Copyright IBM Corp. 2008
+ * Copyright IBM Corp. 2008,2009
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
@@ -40,7 +40,11 @@ struct sca_block {
struct sca_entry cpu[64];
} __attribute__((packed));
-#define KVM_PAGES_PER_HPAGE 256
+#define KVM_NR_PAGE_SIZES 2
+#define KVM_HPAGE_SHIFT(x) (PAGE_SHIFT + ((x) - 1) * 8)
+#define KVM_HPAGE_SIZE(x) (1UL << KVM_HPAGE_SHIFT(x))
+#define KVM_HPAGE_MASK(x) (~(KVM_HPAGE_SIZE(x) - 1))
+#define KVM_PAGES_PER_HPAGE(x) (KVM_HPAGE_SIZE(x) / PAGE_SIZE)
#define CPUSTAT_HOST 0x80000000
#define CPUSTAT_WAIT 0x10000000
@@ -182,8 +186,9 @@ struct kvm_s390_interrupt_info {
};
/* for local_interrupt.action_flags */
-#define ACTION_STORE_ON_STOP 1
-#define ACTION_STOP_ON_STOP 2
+#define ACTION_STORE_ON_STOP (1<<0)
+#define ACTION_STOP_ON_STOP (1<<1)
+#define ACTION_RELOADVCPU_ON_STOP (1<<2)
struct kvm_s390_local_interrupt {
spinlock_t lock;
@@ -227,8 +232,6 @@ struct kvm_vm_stat {
};
struct kvm_arch{
- unsigned long guest_origin;
- unsigned long guest_memsize;
struct sca_block *sca;
debug_info_t *dbf;
struct kvm_s390_float_interrupt float_int;
diff --git a/arch/s390/include/asm/kvm_para.h b/arch/s390/include/asm/kvm_para.h
index 2c503796b61..6964db226f8 100644
--- a/arch/s390/include/asm/kvm_para.h
+++ b/arch/s390/include/asm/kvm_para.h
@@ -13,6 +13,8 @@
#ifndef __S390_KVM_PARA_H
#define __S390_KVM_PARA_H
+#ifdef __KERNEL__
+
/*
* Hypercalls for KVM on s390. The calling convention is similar to the
* s390 ABI, so we use R2-R6 for parameters 1-5. In addition we use R1
@@ -147,4 +149,6 @@ static inline unsigned int kvm_arch_para_features(void)
return 0;
}
+#endif
+
#endif /* __S390_KVM_PARA_H */
diff --git a/arch/s390/include/asm/timex.h b/arch/s390/include/asm/timex.h
index 24aa1cda20a..68d9fea34b4 100644
--- a/arch/s390/include/asm/timex.h
+++ b/arch/s390/include/asm/timex.h
@@ -88,6 +88,14 @@ int get_sync_clock(unsigned long long *clock);
void init_cpu_timer(void);
unsigned long long monotonic_clock(void);
+void tod_to_timeval(__u64, struct timespec *);
+
+static inline
+void stck_to_timespec(unsigned long long stck, struct timespec *ts)
+{
+ tod_to_timeval(stck - TOD_UNIX_EPOCH, ts);
+}
+
extern u64 sched_clock_base_cc;
/**
diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
index be8bceaf37d..4c512561687 100644
--- a/arch/s390/kernel/debug.c
+++ b/arch/s390/kernel/debug.c
@@ -63,8 +63,6 @@ typedef struct
} debug_sprintf_entry_t;
-extern void tod_to_timeval(uint64_t todval, struct timespec *xtime);
-
/* internal function prototyes */
static int debug_init(void);
@@ -1450,17 +1448,13 @@ debug_dflt_header_fn(debug_info_t * id, struct debug_view *view,
int area, debug_entry_t * entry, char *out_buf)
{
struct timespec time_spec;
- unsigned long long time;
char *except_str;
unsigned long caller;
int rc = 0;
unsigned int level;
level = entry->id.fields.level;
- time = entry->id.stck;
- /* adjust todclock to 1970 */
- time -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096);
- tod_to_timeval(time, &time_spec);
+ stck_to_timespec(entry->id.stck, &time_spec);
if (entry->id.fields.exception)
except_str = "*";
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index 54e327e9af0..e3dc28b8075 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -91,6 +91,7 @@ void tod_to_timeval(__u64 todval, struct timespec *xtime)
todval -= (sec * 1000000) << 12;
xtime->tv_nsec = ((todval * 1000) >> 12);
}
+EXPORT_SYMBOL(tod_to_timeval);
void clock_comparator_work(void)
{
diff --git a/arch/s390/kvm/Kconfig b/arch/s390/kvm/Kconfig
index 3e260b7e37b..bf164fc2186 100644
--- a/arch/s390/kvm/Kconfig
+++ b/arch/s390/kvm/Kconfig
@@ -1,11 +1,7 @@
#
# KVM configuration
#
-config HAVE_KVM
- bool
-
-config HAVE_KVM_IRQCHIP
- bool
+source "virt/kvm/Kconfig"
menuconfig VIRTUALIZATION
bool "Virtualization"
@@ -38,9 +34,6 @@ config KVM
If unsure, say N.
-config KVM_TRACE
- bool
-
# OK, it's a little counter-intuitive to do this, but it puts it neatly under
# the virtualization menu.
source drivers/virtio/Kconfig
diff --git a/arch/s390/kvm/gaccess.h b/arch/s390/kvm/gaccess.h
index ed60f3a74a8..03c716a0f01 100644
--- a/arch/s390/kvm/gaccess.h
+++ b/arch/s390/kvm/gaccess.h
@@ -1,7 +1,7 @@
/*
* gaccess.h - access guest memory
*
- * Copyright IBM Corp. 2008
+ * Copyright IBM Corp. 2008,2009
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
@@ -16,13 +16,14 @@
#include <linux/compiler.h>
#include <linux/kvm_host.h>
#include <asm/uaccess.h>
+#include "kvm-s390.h"
static inline void __user *__guestaddr_to_user(struct kvm_vcpu *vcpu,
unsigned long guestaddr)
{
unsigned long prefix = vcpu->arch.sie_block->prefix;
- unsigned long origin = vcpu->kvm->arch.guest_origin;
- unsigned long memsize = vcpu->kvm->arch.guest_memsize;
+ unsigned long origin = vcpu->arch.sie_block->gmsor;
+ unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
if (guestaddr < 2 * PAGE_SIZE)
guestaddr += prefix;
@@ -158,8 +159,8 @@ static inline int copy_to_guest(struct kvm_vcpu *vcpu, unsigned long guestdest,
const void *from, unsigned long n)
{
unsigned long prefix = vcpu->arch.sie_block->prefix;
- unsigned long origin = vcpu->kvm->arch.guest_origin;
- unsigned long memsize = vcpu->kvm->arch.guest_memsize;
+ unsigned long origin = vcpu->arch.sie_block->gmsor;
+ unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
if ((guestdest < 2 * PAGE_SIZE) && (guestdest + n > 2 * PAGE_SIZE))
goto slowpath;
@@ -209,8 +210,8 @@ static inline int copy_from_guest(struct kvm_vcpu *vcpu, void *to,
unsigned long guestsrc, unsigned long n)
{
unsigned long prefix = vcpu->arch.sie_block->prefix;
- unsigned long origin = vcpu->kvm->arch.guest_origin;
- unsigned long memsize = vcpu->kvm->arch.guest_memsize;
+ unsigned long origin = vcpu->arch.sie_block->gmsor;
+ unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
if ((guestsrc < 2 * PAGE_SIZE) && (guestsrc + n > 2 * PAGE_SIZE))
goto slowpath;
@@ -244,8 +245,8 @@ static inline int copy_to_guest_absolute(struct kvm_vcpu *vcpu,
unsigned long guestdest,
const void *from, unsigned long n)
{
- unsigned long origin = vcpu->kvm->arch.guest_origin;
- unsigned long memsize = vcpu->kvm->arch.guest_memsize;
+ unsigned long origin = vcpu->arch.sie_block->gmsor;
+ unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
if (guestdest + n > memsize)
return -EFAULT;
@@ -262,8 +263,8 @@ static inline int copy_from_guest_absolute(struct kvm_vcpu *vcpu, void *to,
unsigned long guestsrc,
unsigned long n)
{
- unsigned long origin = vcpu->kvm->arch.guest_origin;
- unsigned long memsize = vcpu->kvm->arch.guest_memsize;
+ unsigned long origin = vcpu->arch.sie_block->gmsor;
+ unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
if (guestsrc + n > memsize)
return -EFAULT;
diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c
index 98997ccba50..ba9d8a7bc1a 100644
--- a/arch/s390/kvm/intercept.c
+++ b/arch/s390/kvm/intercept.c
@@ -1,7 +1,7 @@
/*
* intercept.c - in-kernel handling for sie intercepts
*
- * Copyright IBM Corp. 2008
+ * Copyright IBM Corp. 2008,2009
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
@@ -128,7 +128,7 @@ static int handle_noop(struct kvm_vcpu *vcpu)
static int handle_stop(struct kvm_vcpu *vcpu)
{
- int rc;
+ int rc = 0;
vcpu->stat.exit_stop_request++;
atomic_clear_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
@@ -141,12 +141,18 @@ static int handle_stop(struct kvm_vcpu *vcpu)
rc = -ENOTSUPP;
}
+ if (vcpu->arch.local_int.action_bits & ACTION_RELOADVCPU_ON_STOP) {
+ vcpu->arch.local_int.action_bits &= ~ACTION_RELOADVCPU_ON_STOP;
+ rc = SIE_INTERCEPT_RERUNVCPU;
+ vcpu->run->exit_reason = KVM_EXIT_INTR;
+ }
+
if (vcpu->arch.local_int.action_bits & ACTION_STOP_ON_STOP) {
vcpu->arch.local_int.action_bits &= ~ACTION_STOP_ON_STOP;
VCPU_EVENT(vcpu, 3, "%s", "cpu stopped");
rc = -ENOTSUPP;
- } else
- rc = 0;
+ }
+
spin_unlock_bh(&vcpu->arch.local_int.lock);
return rc;
}
@@ -158,9 +164,9 @@ static int handle_validity(struct kvm_vcpu *vcpu)
vcpu->stat.exit_validity++;
if ((viwhy == 0x37) && (vcpu->arch.sie_block->prefix
- <= vcpu->kvm->arch.guest_memsize - 2*PAGE_SIZE)){
+ <= kvm_s390_vcpu_get_memsize(vcpu) - 2*PAGE_SIZE)) {
rc = fault_in_pages_writeable((char __user *)
- vcpu->kvm->arch.guest_origin +
+ vcpu->arch.sie_block->gmsor +
vcpu->arch.sie_block->prefix,
2*PAGE_SIZE);
if (rc)
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 4d613415c43..2c2f9835341 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -283,7 +283,7 @@ static int __try_deliver_ckc_interrupt(struct kvm_vcpu *vcpu)
return 1;
}
-int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
+static int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
{
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
struct kvm_s390_float_interrupt *fi = vcpu->arch.local_int.float_int;
@@ -320,12 +320,6 @@ int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
return rc;
}
-int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu)
-{
- /* do real check here */
- return 1;
-}
-
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
{
return 0;
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 90d9d1ba258..07ced89740d 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -1,7 +1,7 @@
/*
* s390host.c -- hosting zSeries kernel virtual machines
*
- * Copyright IBM Corp. 2008
+ * Copyright IBM Corp. 2008,2009
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
@@ -10,6 +10,7 @@
* Author(s): Carsten Otte <cotte@de.ibm.com>
* Christian Borntraeger <borntraeger@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Christian Ehrhardt <ehrhardt@de.ibm.com>
*/
#include <linux/compiler.h>
@@ -210,13 +211,17 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
static void kvm_free_vcpus(struct kvm *kvm)
{
unsigned int i;
+ struct kvm_vcpu *vcpu;
- for (i = 0; i < KVM_MAX_VCPUS; ++i) {
- if (kvm->vcpus[i]) {
- kvm_arch_vcpu_destroy(kvm->vcpus[i]);
- kvm->vcpus[i] = NULL;
- }
- }
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ kvm_arch_vcpu_destroy(vcpu);
+
+ mutex_lock(&kvm->lock);
+ for (i = 0; i < atomic_read(&kvm->online_vcpus); i++)
+ kvm->vcpus[i] = NULL;
+
+ atomic_set(&kvm->online_vcpus, 0);
+ mutex_unlock(&kvm->lock);
}
void kvm_arch_sync_events(struct kvm *kvm)
@@ -278,16 +283,10 @@ static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu)
vcpu->arch.sie_block->gbea = 1;
}
-/* The current code can have up to 256 pages for virtio */
-#define VIRTIODESCSPACE (256ul * 4096ul)
-
int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
{
atomic_set(&vcpu->arch.sie_block->cpuflags, CPUSTAT_ZARCH);
- vcpu->arch.sie_block->gmslm = vcpu->kvm->arch.guest_memsize +
- vcpu->kvm->arch.guest_origin +
- VIRTIODESCSPACE - 1ul;
- vcpu->arch.sie_block->gmsor = vcpu->kvm->arch.guest_origin;
+ set_bit(KVM_REQ_MMU_RELOAD, &vcpu->requests);
vcpu->arch.sie_block->ecb = 2;
vcpu->arch.sie_block->eca = 0xC1002001U;
vcpu->arch.sie_block->fac = (int) (long) facilities;
@@ -319,8 +318,6 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
BUG_ON(!kvm->arch.sca);
if (!kvm->arch.sca->cpu[id].sda)
kvm->arch.sca->cpu[id].sda = (__u64) vcpu->arch.sie_block;
- else
- BUG_ON(!kvm->vcpus[id]); /* vcpu does already exist */
vcpu->arch.sie_block->scaoh = (__u32)(((__u64)kvm->arch.sca) >> 32);
vcpu->arch.sie_block->scaol = (__u32)(__u64)kvm->arch.sca;
@@ -490,9 +487,15 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
vcpu_load(vcpu);
+rerun_vcpu:
+ if (vcpu->requests)
+ if (test_and_clear_bit(KVM_REQ_MMU_RELOAD, &vcpu->requests))
+ kvm_s390_vcpu_set_mem(vcpu);
+
/* verify, that memory has been registered */
- if (!vcpu->kvm->arch.guest_memsize) {
+ if (!vcpu->arch.sie_block->gmslm) {
vcpu_put(vcpu);
+ VCPU_EVENT(vcpu, 3, "%s", "no memory registered to run vcpu");
return -EINVAL;
}
@@ -509,6 +512,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
vcpu->arch.sie_block->gpsw.addr = kvm_run->s390_sieic.addr;
break;
case KVM_EXIT_UNKNOWN:
+ case KVM_EXIT_INTR:
case KVM_EXIT_S390_RESET:
break;
default:
@@ -522,8 +526,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
rc = kvm_handle_sie_intercept(vcpu);
} while (!signal_pending(current) && !rc);
- if (signal_pending(current) && !rc)
+ if (rc == SIE_INTERCEPT_RERUNVCPU)
+ goto rerun_vcpu;
+
+ if (signal_pending(current) && !rc) {
+ kvm_run->exit_reason = KVM_EXIT_INTR;
rc = -EINTR;
+ }
if (rc == -ENOTSUPP) {
/* intercept cannot be handled in-kernel, prepare kvm-run */
@@ -676,6 +685,7 @@ int kvm_arch_set_memory_region(struct kvm *kvm,
int user_alloc)
{
int i;
+ struct kvm_vcpu *vcpu;
/* A few sanity checks. We can have exactly one memory slot which has
to start at guest virtual zero and which has to be located at a
@@ -684,7 +694,7 @@ int kvm_arch_set_memory_region(struct kvm *kvm,
vmas. It is okay to mmap() and munmap() stuff in this slot after
doing this call at any time */
- if (mem->slot || kvm->arch.guest_memsize)
+ if (mem->slot)
return -EINVAL;
if (mem->guest_phys_addr)
@@ -699,36 +709,14 @@ int kvm_arch_set_memory_region(struct kvm *kvm,
if (!user_alloc)
return -EINVAL;
- /* lock all vcpus */
- for (i = 0; i < KVM_MAX_VCPUS; ++i) {
- if (!kvm->vcpus[i])
+ /* request update of sie control block for all available vcpus */
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ if (test_and_set_bit(KVM_REQ_MMU_RELOAD, &vcpu->requests))
continue;
- if (!mutex_trylock(&kvm->vcpus[i]->mutex))
- goto fail_out;
- }
-
- kvm->arch.guest_origin = mem->userspace_addr;
- kvm->arch.guest_memsize = mem->memory_size;
-
- /* update sie control blocks, and unlock all vcpus */
- for (i = 0; i < KVM_MAX_VCPUS; ++i) {
- if (kvm->vcpus[i]) {
- kvm->vcpus[i]->arch.sie_block->gmsor =
- kvm->arch.guest_origin;
- kvm->vcpus[i]->arch.sie_block->gmslm =
- kvm->arch.guest_memsize +
- kvm->arch.guest_origin +
- VIRTIODESCSPACE - 1ul;
- mutex_unlock(&kvm->vcpus[i]->mutex);
- }
+ kvm_s390_inject_sigp_stop(vcpu, ACTION_RELOADVCPU_ON_STOP);
}
return 0;
-
-fail_out:
- for (; i >= 0; i--)
- mutex_unlock(&kvm->vcpus[i]->mutex);
- return -EINVAL;
}
void kvm_arch_flush_shadow(struct kvm *kvm)
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index 748fee87232..ec5eee7c25d 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -1,7 +1,7 @@
/*
* kvm_s390.h - definition for kvm on s390
*
- * Copyright IBM Corp. 2008
+ * Copyright IBM Corp. 2008,2009
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
@@ -9,6 +9,7 @@
*
* Author(s): Carsten Otte <cotte@de.ibm.com>
* Christian Borntraeger <borntraeger@de.ibm.com>
+ * Christian Ehrhardt <ehrhardt@de.ibm.com>
*/
#ifndef ARCH_S390_KVM_S390_H
@@ -18,8 +19,13 @@
#include <linux/kvm.h>
#include <linux/kvm_host.h>
+/* The current code can have up to 256 pages for virtio */
+#define VIRTIODESCSPACE (256ul * 4096ul)
+
typedef int (*intercept_handler_t)(struct kvm_vcpu *vcpu);
+/* negativ values are error codes, positive values for internal conditions */
+#define SIE_INTERCEPT_RERUNVCPU (1<<0)
int kvm_handle_sie_intercept(struct kvm_vcpu *vcpu);
#define VM_EVENT(d_kvm, d_loglevel, d_string, d_args...)\
@@ -50,6 +56,30 @@ int kvm_s390_inject_vm(struct kvm *kvm,
int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
struct kvm_s390_interrupt *s390int);
int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code);
+int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action);
+
+static inline int kvm_s390_vcpu_get_memsize(struct kvm_vcpu *vcpu)
+{
+ return vcpu->arch.sie_block->gmslm
+ - vcpu->arch.sie_block->gmsor
+ - VIRTIODESCSPACE + 1ul;
+}
+
+static inline void kvm_s390_vcpu_set_mem(struct kvm_vcpu *vcpu)
+{
+ struct kvm_memory_slot *mem;
+
+ down_read(&vcpu->kvm->slots_lock);
+ mem = &vcpu->kvm->memslots[0];
+
+ vcpu->arch.sie_block->gmsor = mem->userspace_addr;
+ vcpu->arch.sie_block->gmslm =
+ mem->userspace_addr +
+ (mem->npages << PAGE_SHIFT) +
+ VIRTIODESCSPACE - 1ul;
+
+ up_read(&vcpu->kvm->slots_lock);
+}
/* implemented in priv.c */
int kvm_s390_handle_b2(struct kvm_vcpu *vcpu);
diff --git a/arch/s390/kvm/sigp.c b/arch/s390/kvm/sigp.c
index 0ef81d6776e..40c8c6748cf 100644
--- a/arch/s390/kvm/sigp.c
+++ b/arch/s390/kvm/sigp.c
@@ -1,7 +1,7 @@
/*
* sigp.c - handlinge interprocessor communication
*
- * Copyright IBM Corp. 2008
+ * Copyright IBM Corp. 2008,2009
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
@@ -9,6 +9,7 @@
*
* Author(s): Carsten Otte <cotte@de.ibm.com>
* Christian Borntraeger <borntraeger@de.ibm.com>
+ * Christian Ehrhardt <ehrhardt@de.ibm.com>
*/
#include <linux/kvm.h>
@@ -107,46 +108,57 @@ unlock:
return rc;
}
-static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int store)
+static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action)
{
- struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
- struct kvm_s390_local_interrupt *li;
struct kvm_s390_interrupt_info *inti;
- int rc;
-
- if (cpu_addr >= KVM_MAX_VCPUS)
- return 3; /* not operational */
inti = kzalloc(sizeof(*inti), GFP_KERNEL);
if (!inti)
return -ENOMEM;
-
inti->type = KVM_S390_SIGP_STOP;
- spin_lock(&fi->lock);
- li = fi->local_int[cpu_addr];
- if (li == NULL) {
- rc = 3; /* not operational */
- kfree(inti);
- goto unlock;
- }
spin_lock_bh(&li->lock);
list_add_tail(&inti->list, &li->list);
atomic_set(&li->active, 1);
atomic_set_mask(CPUSTAT_STOP_INT, li->cpuflags);
- if (store)
- li->action_bits |= ACTION_STORE_ON_STOP;
- li->action_bits |= ACTION_STOP_ON_STOP;
+ li->action_bits |= action;
if (waitqueue_active(&li->wq))
wake_up_interruptible(&li->wq);
spin_unlock_bh(&li->lock);
- rc = 0; /* order accepted */
+
+ return 0; /* order accepted */
+}
+
+static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action)
+{
+ struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
+ struct kvm_s390_local_interrupt *li;
+ int rc;
+
+ if (cpu_addr >= KVM_MAX_VCPUS)
+ return 3; /* not operational */
+
+ spin_lock(&fi->lock);
+ li = fi->local_int[cpu_addr];
+ if (li == NULL) {
+ rc = 3; /* not operational */
+ goto unlock;
+ }
+
+ rc = __inject_sigp_stop(li, action);
+
unlock:
spin_unlock(&fi->lock);
VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", cpu_addr);
return rc;
}
+int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action)
+{
+ struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
+ return __inject_sigp_stop(li, action);
+}
+
static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter)
{
int rc;
@@ -177,9 +189,9 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
/* make sure that the new value is valid memory */
address = address & 0x7fffe000u;
if ((copy_from_guest(vcpu, &tmp,
- (u64) (address + vcpu->kvm->arch.guest_origin) , 1)) ||
+ (u64) (address + vcpu->arch.sie_block->gmsor) , 1)) ||
(copy_from_guest(vcpu, &tmp, (u64) (address +
- vcpu->kvm->arch.guest_origin + PAGE_SIZE), 1))) {
+ vcpu->arch.sie_block->gmsor + PAGE_SIZE), 1))) {
*reg |= SIGP_STAT_INVALID_PARAMETER;
return 1; /* invalid parameter */
}
@@ -262,11 +274,11 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
break;
case SIGP_STOP:
vcpu->stat.instruction_sigp_stop++;
- rc = __sigp_stop(vcpu, cpu_addr, 0);
+ rc = __sigp_stop(vcpu, cpu_addr, ACTION_STOP_ON_STOP);
break;
case SIGP_STOP_STORE_STATUS:
vcpu->stat.instruction_sigp_stop++;
- rc = __sigp_stop(vcpu, cpu_addr, 1);
+ rc = __sigp_stop(vcpu, cpu_addr, ACTION_STORE_ON_STOP);
break;
case SIGP_SET_ARCH:
vcpu->stat.instruction_sigp_arch++;
diff --git a/arch/x86/include/asm/apicdef.h b/arch/x86/include/asm/apicdef.h
index 7386bfa4f4b..3b62da926de 100644
--- a/arch/x86/include/asm/apicdef.h
+++ b/arch/x86/include/asm/apicdef.h
@@ -15,6 +15,7 @@
#define APIC_LVR 0x30
#define APIC_LVR_MASK 0xFF00FF
+#define APIC_LVR_DIRECTED_EOI (1 << 24)
#define GET_APIC_VERSION(x) ((x) & 0xFFu)
#define GET_APIC_MAXLVT(x) (((x) >> 16) & 0xFFu)
#ifdef CONFIG_X86_32
@@ -41,6 +42,7 @@
#define APIC_DFR_CLUSTER 0x0FFFFFFFul
#define APIC_DFR_FLAT 0xFFFFFFFFul
#define APIC_SPIV 0xF0
+#define APIC_SPIV_DIRECTED_EOI (1 << 12)
#define APIC_SPIV_FOCUS_DISABLED (1 << 9)
#define APIC_SPIV_APIC_ENABLED (1 << 8)
#define APIC_ISR 0x100
diff --git a/arch/x86/include/asm/kvm.h b/arch/x86/include/asm/kvm.h
index 125be8b1956..4a5fe914dc5 100644
--- a/arch/x86/include/asm/kvm.h
+++ b/arch/x86/include/asm/kvm.h
@@ -17,6 +17,8 @@
#define __KVM_HAVE_USER_NMI
#define __KVM_HAVE_GUEST_DEBUG
#define __KVM_HAVE_MSIX
+#define __KVM_HAVE_MCE
+#define __KVM_HAVE_PIT_STATE2
/* Architectural interrupt line count. */
#define KVM_NR_INTERRUPTS 256
@@ -236,6 +238,14 @@ struct kvm_pit_state {
struct kvm_pit_channel_state channels[3];
};
+#define KVM_PIT_FLAGS_HPET_LEGACY 0x00000001
+
+struct kvm_pit_state2 {
+ struct kvm_pit_channel_state channels[3];
+ __u32 flags;
+ __u32 reserved[9];
+};
+
struct kvm_reinject_control {
__u8 pit_reinject;
__u8 reserved[31];
diff --git a/arch/x86/include/asm/kvm_x86_emulate.h b/arch/x86/include/asm/kvm_emulate.h
index b7ed2c42311..b7ed2c42311 100644
--- a/arch/x86/include/asm/kvm_x86_emulate.h
+++ b/arch/x86/include/asm/kvm_emulate.h
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index eabdc1cfab5..3be000435fa 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/mmu_notifier.h>
+#include <linux/tracepoint.h>
#include <linux/kvm.h>
#include <linux/kvm_para.h>
@@ -37,12 +38,14 @@
#define CR3_L_MODE_RESERVED_BITS (CR3_NONPAE_RESERVED_BITS | \
0xFFFFFF0000000000ULL)
-#define KVM_GUEST_CR0_MASK \
- (X86_CR0_PG | X86_CR0_PE | X86_CR0_WP | X86_CR0_NE \
- | X86_CR0_NW | X86_CR0_CD)
+#define KVM_GUEST_CR0_MASK_UNRESTRICTED_GUEST \
+ (X86_CR0_WP | X86_CR0_NE | X86_CR0_NW | X86_CR0_CD)
+#define KVM_GUEST_CR0_MASK \
+ (KVM_GUEST_CR0_MASK_UNRESTRICTED_GUEST | X86_CR0_PG | X86_CR0_PE)
+#define KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST \
+ (X86_CR0_WP | X86_CR0_NE | X86_CR0_TS | X86_CR0_MP)
#define KVM_VM_CR0_ALWAYS_ON \
- (X86_CR0_PG | X86_CR0_PE | X86_CR0_WP | X86_CR0_NE | X86_CR0_TS \
- | X86_CR0_MP)
+ (KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST | X86_CR0_PG | X86_CR0_PE)
#define KVM_GUEST_CR4_MASK \
(X86_CR4_VME | X86_CR4_PSE | X86_CR4_PAE | X86_CR4_PGE | X86_CR4_VMXE)
#define KVM_PMODE_VM_CR4_ALWAYS_ON (X86_CR4_PAE | X86_CR4_VMXE)
@@ -51,12 +54,12 @@
#define INVALID_PAGE (~(hpa_t)0)
#define UNMAPPED_GVA (~(gpa_t)0)
-/* shadow tables are PAE even on non-PAE hosts */
-#define KVM_HPAGE_SHIFT 21
-#define KVM_HPAGE_SIZE (1UL << KVM_HPAGE_SHIFT)
-#define KVM_HPAGE_MASK (~(KVM_HPAGE_SIZE - 1))
-
-#define KVM_PAGES_PER_HPAGE (KVM_HPAGE_SIZE / PAGE_SIZE)
+/* KVM Hugepage definitions for x86 */
+#define KVM_NR_PAGE_SIZES 3
+#define KVM_HPAGE_SHIFT(x) (PAGE_SHIFT + (((x) - 1) * 9))
+#define KVM_HPAGE_SIZE(x) (1UL << KVM_HPAGE_SHIFT(x))
+#define KVM_HPAGE_MASK(x) (~(KVM_HPAGE_SIZE(x) - 1))
+#define KVM_PAGES_PER_HPAGE(x) (KVM_HPAGE_SIZE(x) / PAGE_SIZE)
#define DE_VECTOR 0
#define DB_VECTOR 1
@@ -120,6 +123,10 @@ enum kvm_reg {
NR_VCPU_REGS
};
+enum kvm_reg_ex {
+ VCPU_EXREG_PDPTR = NR_VCPU_REGS,
+};
+
enum {
VCPU_SREG_ES,
VCPU_SREG_CS,
@@ -131,7 +138,7 @@ enum {
VCPU_SREG_LDTR,
};
-#include <asm/kvm_x86_emulate.h>
+#include <asm/kvm_emulate.h>
#define KVM_NR_MEM_OBJS 40
@@ -308,7 +315,6 @@ struct kvm_vcpu_arch {
struct {
gfn_t gfn; /* presumed gfn during guest pte update */
pfn_t pfn; /* pfn corresponding to that gfn */
- int largepage;
unsigned long mmu_seq;
} update_pte;
@@ -334,16 +340,6 @@ struct kvm_vcpu_arch {
u8 nr;
} interrupt;
- struct {
- int vm86_active;
- u8 save_iopl;
- struct kvm_save_segment {
- u16 selector;
- unsigned long base;
- u32 limit;
- u32 ar;
- } tr, es, ds, fs, gs;
- } rmode;
int halt_request; /* real mode on Intel only */
int cpuid_nent;
@@ -366,13 +362,15 @@ struct kvm_vcpu_arch {
u32 pat;
int switch_db_regs;
- unsigned long host_db[KVM_NR_DB_REGS];
- unsigned long host_dr6;
- unsigned long host_dr7;
unsigned long db[KVM_NR_DB_REGS];
unsigned long dr6;
unsigned long dr7;
unsigned long eff_db[KVM_NR_DB_REGS];
+
+ u64 mcg_cap;
+ u64 mcg_status;
+ u64 mcg_ctl;
+ u64 *mce_banks;
};
struct kvm_mem_alias {
@@ -409,6 +407,7 @@ struct kvm_arch{
struct page *ept_identity_pagetable;
bool ept_identity_pagetable_done;
+ gpa_t ept_identity_map_addr;
unsigned long irq_sources_bitmap;
unsigned long irq_states[KVM_IOAPIC_NUM_PINS];
@@ -526,6 +525,9 @@ struct kvm_x86_ops {
int (*set_tss_addr)(struct kvm *kvm, unsigned int addr);
int (*get_tdp_level)(void);
u64 (*get_mt_mask)(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio);
+ bool (*gb_page_enable)(void);
+
+ const struct trace_print_flags *exit_reasons_str;
};
extern struct kvm_x86_ops *kvm_x86_ops;
@@ -618,6 +620,7 @@ void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr);
void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code);
void kvm_inject_page_fault(struct kvm_vcpu *vcpu, unsigned long cr2,
u32 error_code);
+bool kvm_require_cpl(struct kvm_vcpu *vcpu, int required_cpl);
int kvm_pic_set_irq(void *opaque, int irq, int level);
@@ -752,8 +755,6 @@ static inline void kvm_inject_gp(struct kvm_vcpu *vcpu, u32 error_code)
kvm_queue_exception_e(vcpu, GP_VECTOR, error_code);
}
-#define MSR_IA32_TIME_STAMP_COUNTER 0x010
-
#define TSS_IOPB_BASE_OFFSET 0x66
#define TSS_BASE_SIZE 0x68
#define TSS_IOPB_SIZE (65536 / 8)
@@ -796,5 +797,8 @@ asmlinkage void kvm_handle_fault_on_reboot(void);
int kvm_unmap_hva(struct kvm *kvm, unsigned long hva);
int kvm_age_hva(struct kvm *kvm, unsigned long hva);
int cpuid_maxphyaddr(struct kvm_vcpu *vcpu);
+int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu);
+int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu);
+int kvm_cpu_get_interrupt(struct kvm_vcpu *v);
#endif /* _ASM_X86_KVM_HOST_H */
diff --git a/arch/x86/include/asm/kvm_para.h b/arch/x86/include/asm/kvm_para.h
index b8a3305ae09..c584076a47f 100644
--- a/arch/x86/include/asm/kvm_para.h
+++ b/arch/x86/include/asm/kvm_para.h
@@ -1,6 +1,8 @@
#ifndef _ASM_X86_KVM_PARA_H
#define _ASM_X86_KVM_PARA_H
+#include <linux/types.h>
+
/* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It
* should be used to determine that a VM is running under KVM.
*/
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index 6be7fc254b5..bd5549034a9 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -374,6 +374,7 @@
/* AMD-V MSRs */
#define MSR_VM_CR 0xc0010114
+#define MSR_VM_IGNNE 0xc0010115
#define MSR_VM_HSAVE_PA 0xc0010117
#endif /* _ASM_X86_MSR_INDEX_H */
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 11be5ad2e0e..272514c2d45 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -55,6 +55,7 @@
#define SECONDARY_EXEC_ENABLE_EPT 0x00000002
#define SECONDARY_EXEC_ENABLE_VPID 0x00000020
#define SECONDARY_EXEC_WBINVD_EXITING 0x00000040
+#define SECONDARY_EXEC_UNRESTRICTED_GUEST 0x00000080
#define PIN_BASED_EXT_INTR_MASK 0x00000001
@@ -351,9 +352,16 @@ enum vmcs_field {
#define VMX_EPT_EXTENT_INDIVIDUAL_ADDR 0
#define VMX_EPT_EXTENT_CONTEXT 1
#define VMX_EPT_EXTENT_GLOBAL 2
+
+#define VMX_EPT_EXECUTE_ONLY_BIT (1ull)
+#define VMX_EPT_PAGE_WALK_4_BIT (1ull << 6)
+#define VMX_EPTP_UC_BIT (1ull << 8)
+#define VMX_EPTP_WB_BIT (1ull << 14)
+#define VMX_EPT_2MB_PAGE_BIT (1ull << 16)
#define VMX_EPT_EXTENT_INDIVIDUAL_BIT (1ull << 24)
#define VMX_EPT_EXTENT_CONTEXT_BIT (1ull << 25)
#define VMX_EPT_EXTENT_GLOBAL_BIT (1ull << 26)
+
#define VMX_EPT_DEFAULT_GAW 3
#define VMX_EPT_MAX_GAW 0x4
#define VMX_EPT_MT_EPTE_SHIFT 3
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index 01213048f62..9bfe9d2ea61 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -183,6 +183,11 @@ void mce_log(struct mce *mce)
set_bit(0, &mce_need_notify);
}
+void __weak decode_mce(struct mce *m)
+{
+ return;
+}
+
static void print_mce(struct mce *m)
{
printk(KERN_EMERG
@@ -205,6 +210,8 @@ static void print_mce(struct mce *m)
printk(KERN_EMERG "PROCESSOR %u:%x TIME %llu SOCKET %u APIC %x\n",
m->cpuvendor, m->cpuid, m->time, m->socketid,
m->apicid);
+
+ decode_mce(m);
}
static void print_mce_head(void)
@@ -215,7 +222,10 @@ static void print_mce_head(void)
static void print_mce_tail(void)
{
printk(KERN_EMERG "This is not a software problem!\n"
- "Run through mcelog --ascii to decode and contact your hardware vendor\n");
+#if (!defined(CONFIG_EDAC) || !defined(CONFIG_CPU_SUP_AMD))
+ "Run through mcelog --ascii to decode and contact your hardware vendor\n"
+#endif
+ );
}
#define PANIC_TIMEOUT 5 /* 5 seconds */
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index c664d515f61..63b0ec8d3d4 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -34,7 +34,6 @@
struct kvm_para_state {
u8 mmu_queue[MMU_QUEUE_SIZE];
int mmu_queue_len;
- enum paravirt_lazy_mode mode;
};
static DEFINE_PER_CPU(struct kvm_para_state, para_state);
@@ -77,7 +76,7 @@ static void kvm_deferred_mmu_op(void *buffer, int len)
{
struct kvm_para_state *state = kvm_para_state();
- if (state->mode != PARAVIRT_LAZY_MMU) {
+ if (paravirt_get_lazy_mode() != PARAVIRT_LAZY_MMU) {
kvm_mmu_op(buffer, len);
return;
}
@@ -185,10 +184,7 @@ static void kvm_release_pt(unsigned long pfn)
static void kvm_enter_lazy_mmu(void)
{
- struct kvm_para_state *state = kvm_para_state();
-
paravirt_enter_lazy_mmu();
- state->mode = paravirt_get_lazy_mode();
}
static void kvm_leave_lazy_mmu(void)
@@ -197,7 +193,6 @@ static void kvm_leave_lazy_mmu(void)
mmu_queue_flush(state);
paravirt_leave_lazy_mmu();
- state->mode = paravirt_get_lazy_mode();
}
static void __init paravirt_ops_setup(void)
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index 223af43f152..e5efcdcca31 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -50,8 +50,8 @@ static unsigned long kvm_get_wallclock(void)
struct timespec ts;
int low, high;
- low = (int)__pa(&wall_clock);
- high = ((u64)__pa(&wall_clock) >> 32);
+ low = (int)__pa_symbol(&wall_clock);
+ high = ((u64)__pa_symbol(&wall_clock) >> 32);
native_write_msr(MSR_KVM_WALL_CLOCK, low, high);
vcpu_time = &get_cpu_var(hv_clock);
diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig
index 8600a09e0c6..b84e571f417 100644
--- a/arch/x86/kvm/Kconfig
+++ b/arch/x86/kvm/Kconfig
@@ -1,12 +1,8 @@
#
# KVM configuration
#
-config HAVE_KVM
- bool
-config HAVE_KVM_IRQCHIP
- bool
- default y
+source "virt/kvm/Kconfig"
menuconfig VIRTUALIZATION
bool "Virtualization"
@@ -29,6 +25,9 @@ config KVM
select PREEMPT_NOTIFIERS
select MMU_NOTIFIER
select ANON_INODES
+ select HAVE_KVM_IRQCHIP
+ select HAVE_KVM_EVENTFD
+ select KVM_APIC_ARCHITECTURE
---help---
Support hosting fully virtualized guest machines using hardware
virtualization extensions. You will need a fairly recent
@@ -63,18 +62,6 @@ config KVM_AMD
To compile this as a module, choose M here: the module
will be called kvm-amd.
-config KVM_TRACE
- bool "KVM trace support"
- depends on KVM && SYSFS
- select MARKERS
- select RELAY
- select DEBUG_FS
- default n
- ---help---
- This option allows reading a trace of kvm-related events through
- relayfs. Note the ABI is not considered stable and will be
- modified in future updates.
-
# OK, it's a little counter-intuitive to do this, but it puts it neatly under
# the virtualization menu.
source drivers/lguest/Kconfig
diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
index b43c4efafe8..0e7fe78d0f7 100644
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -1,22 +1,19 @@
-#
-# Makefile for Kernel-based Virtual Machine module
-#
-
-common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \
- coalesced_mmio.o irq_comm.o)
-ifeq ($(CONFIG_KVM_TRACE),y)
-common-objs += $(addprefix ../../../virt/kvm/, kvm_trace.o)
-endif
-ifeq ($(CONFIG_IOMMU_API),y)
-common-objs += $(addprefix ../../../virt/kvm/, iommu.o)
-endif
EXTRA_CFLAGS += -Ivirt/kvm -Iarch/x86/kvm
-kvm-objs := $(common-objs) x86.o mmu.o x86_emulate.o i8259.o irq.o lapic.o \
- i8254.o timer.o
-obj-$(CONFIG_KVM) += kvm.o
-kvm-intel-objs = vmx.o
-obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
-kvm-amd-objs = svm.o
-obj-$(CONFIG_KVM_AMD) += kvm-amd.o
+CFLAGS_x86.o := -I.
+CFLAGS_svm.o := -I.
+CFLAGS_vmx.o := -I.
+
+kvm-y += $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \
+ coalesced_mmio.o irq_comm.o eventfd.o)
+kvm-$(CONFIG_IOMMU_API) += $(addprefix ../../../virt/kvm/, iommu.o)
+
+kvm-y += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \
+ i8254.o timer.o
+kvm-intel-y += vmx.o
+kvm-amd-y += svm.o
+
+obj-$(CONFIG_KVM) += kvm.o
+obj-$(CONFIG_KVM_INTEL) += kvm-intel.o
+obj-$(CONFIG_KVM_AMD) += kvm-amd.o
diff --git a/arch/x86/kvm/x86_emulate.c b/arch/x86/kvm/emulate.c
index 616de4628d6..1be5cd640e9 100644
--- a/arch/x86/kvm/x86_emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -1,5 +1,5 @@
/******************************************************************************
- * x86_emulate.c
+ * emulate.c
*
* Generic x86 (32-bit and 64-bit) instruction decoder and emulator.
*
@@ -30,7 +30,9 @@
#define DPRINTF(x...) do {} while (0)
#endif
#include <linux/module.h>
-#include <asm/kvm_x86_emulate.h>
+#include <asm/kvm_emulate.h>
+
+#include "mmu.h" /* for is_long_mode() */
/*
* Opcode effective-address decode tables.
@@ -60,6 +62,7 @@
#define SrcImmByte (6<<4) /* 8-bit sign-extended immediate operand. */
#define SrcOne (7<<4) /* Implied '1' */
#define SrcImmUByte (8<<4) /* 8-bit unsigned immediate operand. */
+#define SrcImmU (9<<4) /* Immediate operand, unsigned */
#define SrcMask (0xf<<4)
/* Generic ModRM decode. */
#define ModRM (1<<8)
@@ -97,11 +100,11 @@ static u32 opcode_table[256] = {
/* 0x10 - 0x17 */
ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
- 0, 0, 0, 0,
+ ByteOp | DstAcc | SrcImm, DstAcc | SrcImm, 0, 0,
/* 0x18 - 0x1F */
ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
- 0, 0, 0, 0,
+ ByteOp | DstAcc | SrcImm, DstAcc | SrcImm, 0, 0,
/* 0x20 - 0x27 */
ByteOp | DstMem | SrcReg | ModRM, DstMem | SrcReg | ModRM,
ByteOp | DstReg | SrcMem | ModRM, DstReg | SrcMem | ModRM,
@@ -195,7 +198,7 @@ static u32 opcode_table[256] = {
ByteOp | SrcImmUByte, SrcImmUByte,
/* 0xE8 - 0xEF */
SrcImm | Stack, SrcImm | ImplicitOps,
- SrcImm | Src2Imm16, SrcImmByte | ImplicitOps,
+ SrcImmU | Src2Imm16, SrcImmByte | ImplicitOps,
SrcNone | ByteOp | ImplicitOps, SrcNone | ImplicitOps,
SrcNone | ByteOp | ImplicitOps, SrcNone | ImplicitOps,
/* 0xF0 - 0xF7 */
@@ -208,7 +211,7 @@ static u32 opcode_table[256] = {
static u32 twobyte_table[256] = {
/* 0x00 - 0x0F */
- 0, Group | GroupDual | Group7, 0, 0, 0, 0, ImplicitOps, 0,
+ 0, Group | GroupDual | Group7, 0, 0, 0, ImplicitOps, ImplicitOps, 0,
ImplicitOps, ImplicitOps, 0, 0, 0, ImplicitOps | ModRM, 0, 0,
/* 0x10 - 0x1F */
0, 0, 0, 0, 0, 0, 0, 0, ImplicitOps | ModRM, 0, 0, 0, 0, 0, 0, 0,
@@ -216,7 +219,9 @@ static u32 twobyte_table[256] = {
ModRM | ImplicitOps, ModRM, ModRM | ImplicitOps, ModRM, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
/* 0x30 - 0x3F */
- ImplicitOps, 0, ImplicitOps, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ ImplicitOps, 0, ImplicitOps, 0,
+ ImplicitOps, ImplicitOps, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x40 - 0x47 */
DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
@@ -319,8 +324,11 @@ static u32 group2_table[] = {
};
/* EFLAGS bit definitions. */
+#define EFLG_VM (1<<17)
+#define EFLG_RF (1<<16)
#define EFLG_OF (1<<11)
#define EFLG_DF (1<<10)
+#define EFLG_IF (1<<9)
#define EFLG_SF (1<<7)
#define EFLG_ZF (1<<6)
#define EFLG_AF (1<<4)
@@ -1027,6 +1035,7 @@ done_prefixes:
c->src.type = OP_MEM;
break;
case SrcImm:
+ case SrcImmU:
c->src.type = OP_IMM;
c->src.ptr = (unsigned long *)c->eip;
c->src.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
@@ -1044,6 +1053,19 @@ done_prefixes:
c->src.val = insn_fetch(s32, 4, c->eip);
break;
}
+ if ((c->d & SrcMask) == SrcImmU) {
+ switch (c->src.bytes) {
+ case 1:
+ c->src.val &= 0xff;
+ break;
+ case 2:
+ c->src.val &= 0xffff;
+ break;
+ case 4:
+ c->src.val &= 0xffffffff;
+ break;
+ }
+ }
break;
case SrcImmByte:
case SrcImmUByte:
@@ -1375,6 +1397,217 @@ static void toggle_interruptibility(struct x86_emulate_ctxt *ctxt, u32 mask)
ctxt->interruptibility = mask;
}
+static inline void
+setup_syscalls_segments(struct x86_emulate_ctxt *ctxt,
+ struct kvm_segment *cs, struct kvm_segment *ss)
+{
+ memset(cs, 0, sizeof(struct kvm_segment));
+ kvm_x86_ops->get_segment(ctxt->vcpu, cs, VCPU_SREG_CS);
+ memset(ss, 0, sizeof(struct kvm_segment));
+
+ cs->l = 0; /* will be adjusted later */
+ cs->base = 0; /* flat segment */
+ cs->g = 1; /* 4kb granularity */
+ cs->limit = 0xffffffff; /* 4GB limit */
+ cs->type = 0x0b; /* Read, Execute, Accessed */
+ cs->s = 1;
+ cs->dpl = 0; /* will be adjusted later */
+ cs->present = 1;
+ cs->db = 1;
+
+ ss->unusable = 0;
+ ss->base = 0; /* flat segment */
+ ss->limit = 0xffffffff; /* 4GB limit */
+ ss->g = 1; /* 4kb granularity */
+ ss->s = 1;
+ ss->type = 0x03; /* Read/Write, Accessed */
+ ss->db = 1; /* 32bit stack segment */
+ ss->dpl = 0;
+ ss->present = 1;
+}
+
+static int
+emulate_syscall(struct x86_emulate_ctxt *ctxt)
+{
+ struct decode_cache *c = &ctxt->decode;
+ struct kvm_segment cs, ss;
+ u64 msr_data;
+
+ /* syscall is not available in real mode */
+ if (c->lock_prefix || ctxt->mode == X86EMUL_MODE_REAL
+ || !(ctxt->vcpu->arch.cr0 & X86_CR0_PE))
+ return -1;
+
+ setup_syscalls_segments(ctxt, &cs, &ss);
+
+ kvm_x86_ops->get_msr(ctxt->vcpu, MSR_STAR, &msr_data);
+ msr_data >>= 32;
+ cs.selector = (u16)(msr_data & 0xfffc);
+ ss.selector = (u16)(msr_data + 8);
+
+ if (is_long_mode(ctxt->vcpu)) {
+ cs.db = 0;
+ cs.l = 1;
+ }
+ kvm_x86_ops->set_segment(ctxt->vcpu, &cs, VCPU_SREG_CS);
+ kvm_x86_ops->set_segment(ctxt->vcpu, &ss, VCPU_SREG_SS);
+
+ c->regs[VCPU_REGS_RCX] = c->eip;
+ if (is_long_mode(ctxt->vcpu)) {
+#ifdef CONFIG_X86_64
+ c->regs[VCPU_REGS_R11] = ctxt->eflags & ~EFLG_RF;
+
+ kvm_x86_ops->get_msr(ctxt->vcpu,
+ ctxt->mode == X86EMUL_MODE_PROT64 ?
+ MSR_LSTAR : MSR_CSTAR, &msr_data);
+ c->eip = msr_data;
+
+ kvm_x86_ops->get_msr(ctxt->vcpu, MSR_SYSCALL_MASK, &msr_data);
+ ctxt->eflags &= ~(msr_data | EFLG_RF);
+#endif
+ } else {
+ /* legacy mode */
+ kvm_x86_ops->get_msr(ctxt->vcpu, MSR_STAR, &msr_data);
+ c->eip = (u32)msr_data;
+
+ ctxt->eflags &= ~(EFLG_VM | EFLG_IF | EFLG_RF);
+ }
+
+ return 0;
+}
+
+static int
+emulate_sysenter(struct x86_emulate_ctxt *ctxt)
+{
+ struct decode_cache *c = &ctxt->decode;
+ struct kvm_segment cs, ss;
+ u64 msr_data;
+
+ /* inject #UD if LOCK prefix is used */
+ if (c->lock_prefix)
+ return -1;
+
+ /* inject #GP if in real mode or paging is disabled */
+ if (ctxt->mode == X86EMUL_MODE_REAL ||
+ !(ctxt->vcpu->arch.cr0 & X86_CR0_PE)) {
+ kvm_inject_gp(ctxt->vcpu, 0);
+ return -1;
+ }
+
+ /* XXX sysenter/sysexit have not been tested in 64bit mode.
+ * Therefore, we inject an #UD.
+ */
+ if (ctxt->mode == X86EMUL_MODE_PROT64)
+ return -1;
+
+ setup_syscalls_segments(ctxt, &cs, &ss);
+
+ kvm_x86_ops->get_msr(ctxt->vcpu, MSR_IA32_SYSENTER_CS, &msr_data);
+ switch (ctxt->mode) {
+ case X86EMUL_MODE_PROT32:
+ if ((msr_data & 0xfffc) == 0x0) {
+ kvm_inject_gp(ctxt->vcpu, 0);
+ return -1;
+ }
+ break;
+ case X86EMUL_MODE_PROT64:
+ if (msr_data == 0x0) {
+ kvm_inject_gp(ctxt->vcpu, 0);
+ return -1;
+ }
+ break;
+ }
+
+ ctxt->eflags &= ~(EFLG_VM | EFLG_IF | EFLG_RF);
+ cs.selector = (u16)msr_data;
+ cs.selector &= ~SELECTOR_RPL_MASK;
+ ss.selector = cs.selector + 8;
+ ss.selector &= ~SELECTOR_RPL_MASK;
+ if (ctxt->mode == X86EMUL_MODE_PROT64
+ || is_long_mode(ctxt->vcpu)) {
+ cs.db = 0;
+ cs.l = 1;
+ }
+
+ kvm_x86_ops->set_segment(ctxt->vcpu, &cs, VCPU_SREG_CS);
+ kvm_x86_ops->set_segment(ctxt->vcpu, &ss, VCPU_SREG_SS);
+
+ kvm_x86_ops->get_msr(ctxt->vcpu, MSR_IA32_SYSENTER_EIP, &msr_data);
+ c->eip = msr_data;
+
+ kvm_x86_ops->get_msr(ctxt->vcpu, MSR_IA32_SYSENTER_ESP, &msr_data);
+ c->regs[VCPU_REGS_RSP] = msr_data;
+
+ return 0;
+}
+
+static int
+emulate_sysexit(struct x86_emulate_ctxt *ctxt)
+{
+ struct decode_cache *c = &ctxt->decode;
+ struct kvm_segment cs, ss;
+ u64 msr_data;
+ int usermode;
+
+ /* inject #UD if LOCK prefix is used */
+ if (c->lock_prefix)
+ return -1;
+
+ /* inject #GP if in real mode or paging is disabled */
+ if (ctxt->mode == X86EMUL_MODE_REAL
+ || !(ctxt->vcpu->arch.cr0 & X86_CR0_PE)) {
+ kvm_inject_gp(ctxt->vcpu, 0);
+ return -1;
+ }
+
+ /* sysexit must be called from CPL 0 */
+ if (kvm_x86_ops->get_cpl(ctxt->vcpu) != 0) {
+ kvm_inject_gp(ctxt->vcpu, 0);
+ return -1;
+ }
+
+ setup_syscalls_segments(ctxt, &cs, &ss);
+
+ if ((c->rex_prefix & 0x8) != 0x0)
+ usermode = X86EMUL_MODE_PROT64;
+ else
+ usermode = X86EMUL_MODE_PROT32;
+
+ cs.dpl = 3;
+ ss.dpl = 3;
+ kvm_x86_ops->get_msr(ctxt->vcpu, MSR_IA32_SYSENTER_CS, &msr_data);
+ switch (usermode) {
+ case X86EMUL_MODE_PROT32:
+ cs.selector = (u16)(msr_data + 16);
+ if ((msr_data & 0xfffc) == 0x0) {
+ kvm_inject_gp(ctxt->vcpu, 0);
+ return -1;
+ }
+ ss.selector = (u16)(msr_data + 24);
+ break;
+ case X86EMUL_MODE_PROT64:
+ cs.selector = (u16)(msr_data + 32);
+ if (msr_data == 0x0) {
+ kvm_inject_gp(ctxt->vcpu, 0);
+ return -1;
+ }
+ ss.selector = cs.selector + 8;
+ cs.db = 0;
+ cs.l = 1;
+ break;
+ }
+ cs.selector |= SELECTOR_RPL_MASK;
+ ss.selector |= SELECTOR_RPL_MASK;
+
+ kvm_x86_ops->set_segment(ctxt->vcpu, &cs, VCPU_SREG_CS);
+ kvm_x86_ops->set_segment(ctxt->vcpu, &ss, VCPU_SREG_SS);
+
+ c->eip = ctxt->vcpu->arch.regs[VCPU_REGS_RDX];
+ c->regs[VCPU_REGS_RSP] = ctxt->vcpu->arch.regs[VCPU_REGS_RCX];
+
+ return 0;
+}
+
int
x86_emulate_insn(struct x86_emulate_ctxt *ctxt, struct x86_emulate_ops *ops)
{
@@ -1970,6 +2203,12 @@ twobyte_insn:
goto cannot_emulate;
}
break;
+ case 0x05: /* syscall */
+ if (emulate_syscall(ctxt) == -1)
+ goto cannot_emulate;
+ else
+ goto writeback;
+ break;
case 0x06:
emulate_clts(ctxt->vcpu);
c->dst.type = OP_NONE;
@@ -2036,6 +2275,18 @@ twobyte_insn:
rc = X86EMUL_CONTINUE;
c->dst.type = OP_NONE;
break;
+ case 0x34: /* sysenter */
+ if (emulate_sysenter(ctxt) == -1)
+ goto cannot_emulate;
+ else
+ goto writeback;
+ break;
+ case 0x35: /* sysexit */
+ if (emulate_sysexit(ctxt) == -1)
+ goto cannot_emulate;
+ else
+ goto writeback;
+ break;
case 0x40 ... 0x4f: /* cmov */
c->dst.val = c->dst.orig_val = c->src.val;
if (!test_cc(c->b, ctxt->eflags))
diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c
index 21f68e00524..82ad523b490 100644
--- a/arch/x86/kvm/i8254.c
+++ b/arch/x86/kvm/i8254.c
@@ -231,7 +231,7 @@ int pit_has_pending_timer(struct kvm_vcpu *vcpu)
{
struct kvm_pit *pit = vcpu->kvm->arch.vpit;
- if (pit && vcpu->vcpu_id == 0 && pit->pit_state.irq_ack)
+ if (pit && kvm_vcpu_is_bsp(vcpu) && pit->pit_state.irq_ack)
return atomic_read(&pit->pit_state.pit_timer.pending);
return 0;
}
@@ -252,7 +252,7 @@ void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu)
struct kvm_pit *pit = vcpu->kvm->arch.vpit;
struct hrtimer *timer;
- if (vcpu->vcpu_id != 0 || !pit)
+ if (!kvm_vcpu_is_bsp(vcpu) || !pit)
return;
timer = &pit->pit_state.pit_timer.timer;
@@ -294,7 +294,7 @@ static void create_pit_timer(struct kvm_kpit_state *ps, u32 val, int is_period)
pt->timer.function = kvm_timer_fn;
pt->t_ops = &kpit_ops;
pt->kvm = ps->pit->kvm;
- pt->vcpu_id = 0;
+ pt->vcpu = pt->kvm->bsp_vcpu;
atomic_set(&pt->pending, 0);
ps->irq_ack = 1;
@@ -332,33 +332,62 @@ static void pit_load_count(struct kvm *kvm, int channel, u32 val)
case 1:
/* FIXME: enhance mode 4 precision */
case 4:
- create_pit_timer(ps, val, 0);
+ if (!(ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)) {
+ create_pit_timer(ps, val, 0);
+ }
break;
case 2:
case 3:
- create_pit_timer(ps, val, 1);
+ if (!(ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)){
+ create_pit_timer(ps, val, 1);
+ }
break;
default:
destroy_pit_timer(&ps->pit_timer);
}
}
-void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val)
+void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val, int hpet_legacy_start)
+{
+ u8 saved_mode;
+ if (hpet_legacy_start) {
+ /* save existing mode for later reenablement */
+ saved_mode = kvm->arch.vpit->pit_state.channels[0].mode;
+ kvm->arch.vpit->pit_state.channels[0].mode = 0xff; /* disable timer */
+ pit_load_count(kvm, channel, val);
+ kvm->arch.vpit->pit_state.channels[0].mode = saved_mode;
+ } else {
+ pit_load_count(kvm, channel, val);
+ }
+}
+
+static inline struct kvm_pit *dev_to_pit(struct kvm_io_device *dev)
+{
+ return container_of(dev, struct kvm_pit, dev);
+}
+
+static inline struct kvm_pit *speaker_to_pit(struct kvm_io_device *dev)
{
- mutex_lock(&kvm->arch.vpit->pit_state.lock);
- pit_load_count(kvm, channel, val);
- mutex_unlock(&kvm->arch.vpit->pit_state.lock);
+ return container_of(dev, struct kvm_pit, speaker_dev);
}
-static void pit_ioport_write(struct kvm_io_device *this,
- gpa_t addr, int len, const void *data)
+static inline int pit_in_range(gpa_t addr)
{
- struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ return ((addr >= KVM_PIT_BASE_ADDRESS) &&
+ (addr < KVM_PIT_BASE_ADDRESS + KVM_PIT_MEM_LENGTH));
+}
+
+static int pit_ioport_write(struct kvm_io_device *this,
+ gpa_t addr, int len, const void *data)
+{
+ struct kvm_pit *pit = dev_to_pit(this);
struct kvm_kpit_state *pit_state = &pit->pit_state;
struct kvm *kvm = pit->kvm;
int channel, access;
struct kvm_kpit_channel_state *s;
u32 val = *(u32 *) data;
+ if (!pit_in_range(addr))
+ return -EOPNOTSUPP;
val &= 0xff;
addr &= KVM_PIT_CHANNEL_MASK;
@@ -421,16 +450,19 @@ static void pit_ioport_write(struct kvm_io_device *this,
}
mutex_unlock(&pit_state->lock);
+ return 0;
}
-static void pit_ioport_read(struct kvm_io_device *this,
- gpa_t addr, int len, void *data)
+static int pit_ioport_read(struct kvm_io_device *this,
+ gpa_t addr, int len, void *data)
{
- struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_pit *pit = dev_to_pit(this);
struct kvm_kpit_state *pit_state = &pit->pit_state;
struct kvm *kvm = pit->kvm;
int ret, count;
struct kvm_kpit_channel_state *s;
+ if (!pit_in_range(addr))
+ return -EOPNOTSUPP;
addr &= KVM_PIT_CHANNEL_MASK;
s = &pit_state->channels[addr];
@@ -485,37 +517,36 @@ static void pit_ioport_read(struct kvm_io_device *this,
memcpy(data, (char *)&ret, len);
mutex_unlock(&pit_state->lock);
+ return 0;
}
-static int pit_in_range(struct kvm_io_device *this, gpa_t addr,
- int len, int is_write)
-{
- return ((addr >= KVM_PIT_BASE_ADDRESS) &&
- (addr < KVM_PIT_BASE_ADDRESS + KVM_PIT_MEM_LENGTH));
-}
-
-static void speaker_ioport_write(struct kvm_io_device *this,
- gpa_t addr, int len, const void *data)
+static int speaker_ioport_write(struct kvm_io_device *this,
+ gpa_t addr, int len, const void *data)
{
- struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_pit *pit = speaker_to_pit(this);
struct kvm_kpit_state *pit_state = &pit->pit_state;
struct kvm *kvm = pit->kvm;
u32 val = *(u32 *) data;
+ if (addr != KVM_SPEAKER_BASE_ADDRESS)
+ return -EOPNOTSUPP;
mutex_lock(&pit_state->lock);
pit_state->speaker_data_on = (val >> 1) & 1;
pit_set_gate(kvm, 2, val & 1);
mutex_unlock(&pit_state->lock);
+ return 0;
}
-static void speaker_ioport_read(struct kvm_io_device *this,
- gpa_t addr, int len, void *data)
+static int speaker_ioport_read(struct kvm_io_device *this,
+ gpa_t addr, int len, void *data)
{
- struct kvm_pit *pit = (struct kvm_pit *)this->private;
+ struct kvm_pit *pit = speaker_to_pit(this);
struct kvm_kpit_state *pit_state = &pit->pit_state;
struct kvm *kvm = pit->kvm;
unsigned int refresh_clock;
int ret;
+ if (addr != KVM_SPEAKER_BASE_ADDRESS)
+ return -EOPNOTSUPP;
/* Refresh clock toggles at about 15us. We approximate as 2^14ns. */
refresh_clock = ((unsigned int)ktime_to_ns(ktime_get()) >> 14) & 1;
@@ -527,12 +558,7 @@ static void speaker_ioport_read(struct kvm_io_device *this,
len = sizeof(ret);
memcpy(data, (char *)&ret, len);
mutex_unlock(&pit_state->lock);
-}
-
-static int speaker_in_range(struct kvm_io_device *this, gpa_t addr,
- int len, int is_write)
-{
- return (addr == KVM_SPEAKER_BASE_ADDRESS);
+ return 0;
}
void kvm_pit_reset(struct kvm_pit *pit)
@@ -541,6 +567,7 @@ void kvm_pit_reset(struct kvm_pit *pit)
struct kvm_kpit_channel_state *c;
mutex_lock(&pit->pit_state.lock);
+ pit->pit_state.flags = 0;
for (i = 0; i < 3; i++) {
c = &pit->pit_state.channels[i];
c->mode = 0xff;
@@ -563,10 +590,22 @@ static void pit_mask_notifer(struct kvm_irq_mask_notifier *kimn, bool mask)
}
}
-struct kvm_pit *kvm_create_pit(struct kvm *kvm)
+static const struct kvm_io_device_ops pit_dev_ops = {
+ .read = pit_ioport_read,
+ .write = pit_ioport_write,
+};
+
+static const struct kvm_io_device_ops speaker_dev_ops = {
+ .read = speaker_ioport_read,
+ .write = speaker_ioport_write,
+};
+
+/* Caller must have writers lock on slots_lock */
+struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags)
{
struct kvm_pit *pit;
struct kvm_kpit_state *pit_state;
+ int ret;
pit = kzalloc(sizeof(struct kvm_pit), GFP_KERNEL);
if (!pit)
@@ -582,19 +621,6 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm)
mutex_lock(&pit->pit_state.lock);
spin_lock_init(&pit->pit_state.inject_lock);
- /* Initialize PIO device */
- pit->dev.read = pit_ioport_read;
- pit->dev.write = pit_ioport_write;
- pit->dev.in_range = pit_in_range;
- pit->dev.private = pit;
- kvm_io_bus_register_dev(&kvm->pio_bus, &pit->dev);
-
- pit->speaker_dev.read = speaker_ioport_read;
- pit->speaker_dev.write = speaker_ioport_write;
- pit->speaker_dev.in_range = speaker_in_range;
- pit->speaker_dev.private = pit;
- kvm_io_bus_register_dev(&kvm->pio_bus, &pit->speaker_dev);
-
kvm->arch.vpit = pit;
pit->kvm = kvm;
@@ -613,7 +639,30 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm)
pit->mask_notifier.func = pit_mask_notifer;
kvm_register_irq_mask_notifier(kvm, 0, &pit->mask_notifier);
+ kvm_iodevice_init(&pit->dev, &pit_dev_ops);
+ ret = __kvm_io_bus_register_dev(&kvm->pio_bus, &pit->dev);
+ if (ret < 0)
+ goto fail;
+
+ if (flags & KVM_PIT_SPEAKER_DUMMY) {
+ kvm_iodevice_init(&pit->speaker_dev, &speaker_dev_ops);
+ ret = __kvm_io_bus_register_dev(&kvm->pio_bus,
+ &pit->speaker_dev);
+ if (ret < 0)
+ goto fail_unregister;
+ }
+
return pit;
+
+fail_unregister:
+ __kvm_io_bus_unregister_dev(&kvm->pio_bus, &pit->dev);
+
+fail:
+ if (pit->irq_source_id >= 0)
+ kvm_free_irq_source_id(kvm, pit->irq_source_id);
+
+ kfree(pit);
+ return NULL;
}
void kvm_free_pit(struct kvm *kvm)
@@ -623,6 +672,8 @@ void kvm_free_pit(struct kvm *kvm)
if (kvm->arch.vpit) {
kvm_unregister_irq_mask_notifier(kvm, 0,
&kvm->arch.vpit->mask_notifier);
+ kvm_unregister_irq_ack_notifier(kvm,
+ &kvm->arch.vpit->pit_state.irq_ack_notifier);
mutex_lock(&kvm->arch.vpit->pit_state.lock);
timer = &kvm->arch.vpit->pit_state.pit_timer.timer;
hrtimer_cancel(timer);
@@ -637,10 +688,10 @@ static void __inject_pit_timer_intr(struct kvm *kvm)
struct kvm_vcpu *vcpu;
int i;
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->irq_lock);
kvm_set_irq(kvm, kvm->arch.vpit->irq_source_id, 0, 1);
kvm_set_irq(kvm, kvm->arch.vpit->irq_source_id, 0, 0);
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->irq_lock);
/*
* Provides NMI watchdog support via Virtual Wire mode.
@@ -652,11 +703,8 @@ static void __inject_pit_timer_intr(struct kvm *kvm)
* VCPU0, and only if its LVT0 is in EXTINT mode.
*/
if (kvm->arch.vapics_in_nmi_mode > 0)
- for (i = 0; i < KVM_MAX_VCPUS; ++i) {
- vcpu = kvm->vcpus[i];
- if (vcpu)
- kvm_apic_nmi_wd_deliver(vcpu);
- }
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ kvm_apic_nmi_wd_deliver(vcpu);
}
void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu)
@@ -665,7 +713,7 @@ void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu)
struct kvm *kvm = vcpu->kvm;
struct kvm_kpit_state *ps;
- if (vcpu && pit) {
+ if (pit) {
int inject = 0;
ps = &pit->pit_state;
diff --git a/arch/x86/kvm/i8254.h b/arch/x86/kvm/i8254.h
index bbd863ff60b..d4c1c7ffdc0 100644
--- a/arch/x86/kvm/i8254.h
+++ b/arch/x86/kvm/i8254.h
@@ -21,6 +21,7 @@ struct kvm_kpit_channel_state {
struct kvm_kpit_state {
struct kvm_kpit_channel_state channels[3];
+ u32 flags;
struct kvm_timer pit_timer;
bool is_periodic;
u32 speaker_data_on;
@@ -49,8 +50,8 @@ struct kvm_pit {
#define KVM_PIT_CHANNEL_MASK 0x3
void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu);
-void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val);
-struct kvm_pit *kvm_create_pit(struct kvm *kvm);
+void kvm_pit_load_count(struct kvm *kvm, int channel, u32 val, int hpet_legacy_start);
+struct kvm_pit *kvm_create_pit(struct kvm *kvm, u32 flags);
void kvm_free_pit(struct kvm *kvm);
void kvm_pit_reset(struct kvm_pit *pit);
diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c
index 1ccb50c74f1..01f15168280 100644
--- a/arch/x86/kvm/i8259.c
+++ b/arch/x86/kvm/i8259.c
@@ -30,50 +30,24 @@
#include "irq.h"
#include <linux/kvm_host.h>
-
-static void pic_lock(struct kvm_pic *s)
- __acquires(&s->lock)
-{
- spin_lock(&s->lock);
-}
-
-static void pic_unlock(struct kvm_pic *s)
- __releases(&s->lock)
-{
- struct kvm *kvm = s->kvm;
- unsigned acks = s->pending_acks;
- bool wakeup = s->wakeup_needed;
- struct kvm_vcpu *vcpu;
-
- s->pending_acks = 0;
- s->wakeup_needed = false;
-
- spin_unlock(&s->lock);
-
- while (acks) {
- kvm_notify_acked_irq(kvm, SELECT_PIC(__ffs(acks)),
- __ffs(acks));
- acks &= acks - 1;
- }
-
- if (wakeup) {
- vcpu = s->kvm->vcpus[0];
- if (vcpu)
- kvm_vcpu_kick(vcpu);
- }
-}
+#include "trace.h"
static void pic_clear_isr(struct kvm_kpic_state *s, int irq)
{
s->isr &= ~(1 << irq);
s->isr_ack |= (1 << irq);
+ if (s != &s->pics_state->pics[0])
+ irq += 8;
+ kvm_notify_acked_irq(s->pics_state->kvm, SELECT_PIC(irq), irq);
}
void kvm_pic_clear_isr_ack(struct kvm *kvm)
{
struct kvm_pic *s = pic_irqchip(kvm);
+ spin_lock(&s->lock);
s->pics[0].isr_ack = 0xff;
s->pics[1].isr_ack = 0xff;
+ spin_unlock(&s->lock);
}
/*
@@ -174,9 +148,9 @@ static void pic_update_irq(struct kvm_pic *s)
void kvm_pic_update_irq(struct kvm_pic *s)
{
- pic_lock(s);
+ spin_lock(&s->lock);
pic_update_irq(s);
- pic_unlock(s);
+ spin_unlock(&s->lock);
}
int kvm_pic_set_irq(void *opaque, int irq, int level)
@@ -184,12 +158,14 @@ int kvm_pic_set_irq(void *opaque, int irq, int level)
struct kvm_pic *s = opaque;
int ret = -1;
- pic_lock(s);
+ spin_lock(&s->lock);
if (irq >= 0 && irq < PIC_NUM_PINS) {
ret = pic_set_irq1(&s->pics[irq >> 3], irq & 7, level);
pic_update_irq(s);
+ trace_kvm_pic_set_irq(irq >> 3, irq & 7, s->pics[irq >> 3].elcr,
+ s->pics[irq >> 3].imr, ret == 0);
}
- pic_unlock(s);
+ spin_unlock(&s->lock);
return ret;
}
@@ -217,7 +193,7 @@ int kvm_pic_read_irq(struct kvm *kvm)
int irq, irq2, intno;
struct kvm_pic *s = pic_irqchip(kvm);
- pic_lock(s);
+ spin_lock(&s->lock);
irq = pic_get_irq(&s->pics[0]);
if (irq >= 0) {
pic_intack(&s->pics[0], irq);
@@ -242,8 +218,7 @@ int kvm_pic_read_irq(struct kvm *kvm)
intno = s->pics[0].irq_base + irq;
}
pic_update_irq(s);
- pic_unlock(s);
- kvm_notify_acked_irq(kvm, SELECT_PIC(irq), irq);
+ spin_unlock(&s->lock);
return intno;
}
@@ -252,7 +227,7 @@ void kvm_pic_reset(struct kvm_kpic_state *s)
{
int irq, irqbase, n;
struct kvm *kvm = s->pics_state->irq_request_opaque;
- struct kvm_vcpu *vcpu0 = kvm->vcpus[0];
+ struct kvm_vcpu *vcpu0 = kvm->bsp_vcpu;
if (s == &s->pics_state->pics[0])
irqbase = 0;
@@ -263,7 +238,7 @@ void kvm_pic_reset(struct kvm_kpic_state *s)
if (vcpu0 && kvm_apic_accept_pic_intr(vcpu0))
if (s->irr & (1 << irq) || s->isr & (1 << irq)) {
n = irq + irqbase;
- s->pics_state->pending_acks |= 1 << n;
+ kvm_notify_acked_irq(kvm, SELECT_PIC(n), n);
}
}
s->last_irr = 0;
@@ -428,8 +403,7 @@ static u32 elcr_ioport_read(void *opaque, u32 addr1)
return s->elcr;
}
-static int picdev_in_range(struct kvm_io_device *this, gpa_t addr,
- int len, int is_write)
+static int picdev_in_range(gpa_t addr)
{
switch (addr) {
case 0x20:
@@ -444,18 +418,25 @@ static int picdev_in_range(struct kvm_io_device *this, gpa_t addr,
}
}
-static void picdev_write(struct kvm_io_device *this,
+static inline struct kvm_pic *to_pic(struct kvm_io_device *dev)
+{
+ return container_of(dev, struct kvm_pic, dev);
+}
+
+static int picdev_write(struct kvm_io_device *this,
gpa_t addr, int len, const void *val)
{
- struct kvm_pic *s = this->private;
+ struct kvm_pic *s = to_pic(this);
unsigned char data = *(unsigned char *)val;
+ if (!picdev_in_range(addr))
+ return -EOPNOTSUPP;
if (len != 1) {
if (printk_ratelimit())
printk(KERN_ERR "PIC: non byte write\n");
- return;
+ return 0;
}
- pic_lock(s);
+ spin_lock(&s->lock);
switch (addr) {
case 0x20:
case 0x21:
@@ -468,21 +449,24 @@ static void picdev_write(struct kvm_io_device *this,
elcr_ioport_write(&s->pics[addr & 1], addr, data);
break;
}
- pic_unlock(s);
+ spin_unlock(&s->lock);
+ return 0;
}
-static void picdev_read(struct kvm_io_device *this,
- gpa_t addr, int len, void *val)
+static int picdev_read(struct kvm_io_device *this,
+ gpa_t addr, int len, void *val)
{
- struct kvm_pic *s = this->private;
+ struct kvm_pic *s = to_pic(this);
unsigned char data = 0;
+ if (!picdev_in_range(addr))
+ return -EOPNOTSUPP;
if (len != 1) {
if (printk_ratelimit())
printk(KERN_ERR "PIC: non byte read\n");
- return;
+ return 0;
}
- pic_lock(s);
+ spin_lock(&s->lock);
switch (addr) {
case 0x20:
case 0x21:
@@ -496,7 +480,8 @@ static void picdev_read(struct kvm_io_device *this,
break;
}
*(unsigned char *)val = data;
- pic_unlock(s);
+ spin_unlock(&s->lock);
+ return 0;
}
/*
@@ -505,20 +490,27 @@ static void picdev_read(struct kvm_io_device *this,
static void pic_irq_request(void *opaque, int level)
{
struct kvm *kvm = opaque;
- struct kvm_vcpu *vcpu = kvm->vcpus[0];
+ struct kvm_vcpu *vcpu = kvm->bsp_vcpu;
struct kvm_pic *s = pic_irqchip(kvm);
int irq = pic_get_irq(&s->pics[0]);
s->output = level;
if (vcpu && level && (s->pics[0].isr_ack & (1 << irq))) {
s->pics[0].isr_ack &= ~(1 << irq);
- s->wakeup_needed = true;
+ kvm_vcpu_kick(vcpu);
}
}
+static const struct kvm_io_device_ops picdev_ops = {
+ .read = picdev_read,
+ .write = picdev_write,
+};
+
struct kvm_pic *kvm_create_pic(struct kvm *kvm)
{
struct kvm_pic *s;
+ int ret;
+
s = kzalloc(sizeof(struct kvm_pic), GFP_KERNEL);
if (!s)
return NULL;
@@ -534,10 +526,12 @@ struct kvm_pic *kvm_create_pic(struct kvm *kvm)
/*
* Initialize PIO device
*/
- s->dev.read = picdev_read;
- s->dev.write = picdev_write;
- s->dev.in_range = picdev_in_range;
- s->dev.private = s;
- kvm_io_bus_register_dev(&kvm->pio_bus, &s->dev);
+ kvm_iodevice_init(&s->dev, &picdev_ops);
+ ret = kvm_io_bus_register_dev(kvm, &kvm->pio_bus, &s->dev);
+ if (ret < 0) {
+ kfree(s);
+ return NULL;
+ }
+
return s;
}
diff --git a/arch/x86/kvm/irq.h b/arch/x86/kvm/irq.h
index 9f593188129..7d6058a2fd3 100644
--- a/arch/x86/kvm/irq.h
+++ b/arch/x86/kvm/irq.h
@@ -63,7 +63,6 @@ struct kvm_kpic_state {
struct kvm_pic {
spinlock_t lock;
- bool wakeup_needed;
unsigned pending_acks;
struct kvm *kvm;
struct kvm_kpic_state pics[2]; /* 0 is master pic, 1 is slave pic */
diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h
index 1ff819dce7d..7bcc5b6a440 100644
--- a/arch/x86/kvm/kvm_cache_regs.h
+++ b/arch/x86/kvm/kvm_cache_regs.h
@@ -29,4 +29,13 @@ static inline void kvm_rip_write(struct kvm_vcpu *vcpu, unsigned long val)
kvm_register_write(vcpu, VCPU_REGS_RIP, val);
}
+static inline u64 kvm_pdptr_read(struct kvm_vcpu *vcpu, int index)
+{
+ if (!test_bit(VCPU_EXREG_PDPTR,
+ (unsigned long *)&vcpu->arch.regs_avail))
+ kvm_x86_ops->cache_reg(vcpu, VCPU_EXREG_PDPTR);
+
+ return vcpu->arch.pdptrs[index];
+}
+
#endif
diff --git a/arch/x86/kvm/kvm_svm.h b/arch/x86/kvm/kvm_svm.h
deleted file mode 100644
index ed66e4c078d..00000000000
--- a/arch/x86/kvm/kvm_svm.h
+++ /dev/null
@@ -1,51 +0,0 @@
-#ifndef __KVM_SVM_H
-#define __KVM_SVM_H
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/list.h>
-#include <linux/kvm_host.h>
-#include <asm/msr.h>
-
-#include <asm/svm.h>
-
-static const u32 host_save_user_msrs[] = {
-#ifdef CONFIG_X86_64
- MSR_STAR, MSR_LSTAR, MSR_CSTAR, MSR_SYSCALL_MASK, MSR_KERNEL_GS_BASE,
- MSR_FS_BASE,
-#endif
- MSR_IA32_SYSENTER_CS, MSR_IA32_SYSENTER_ESP, MSR_IA32_SYSENTER_EIP,
-};
-
-#define NR_HOST_SAVE_USER_MSRS ARRAY_SIZE(host_save_user_msrs)
-
-struct kvm_vcpu;
-
-struct vcpu_svm {
- struct kvm_vcpu vcpu;
- struct vmcb *vmcb;
- unsigned long vmcb_pa;
- struct svm_cpu_data *svm_data;
- uint64_t asid_generation;
-
- u64 next_rip;
-
- u64 host_user_msrs[NR_HOST_SAVE_USER_MSRS];
- u64 host_gs_base;
- unsigned long host_cr2;
-
- u32 *msrpm;
- struct vmcb *hsave;
- u64 hsave_msr;
-
- u64 nested_vmcb;
-
- /* These are the merged vectors */
- u32 *nested_msrpm;
-
- /* gpa pointers to the real vectors */
- u64 nested_vmcb_msrpm;
-};
-
-#endif
-
diff --git a/arch/x86/kvm/kvm_timer.h b/arch/x86/kvm/kvm_timer.h
index 26bd6ba74e1..55c7524dda5 100644
--- a/arch/x86/kvm/kvm_timer.h
+++ b/arch/x86/kvm/kvm_timer.h
@@ -6,7 +6,7 @@ struct kvm_timer {
bool reinject;
struct kvm_timer_ops *t_ops;
struct kvm *kvm;
- int vcpu_id;
+ struct kvm_vcpu *vcpu;
};
struct kvm_timer_ops {
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index ae99d83f81a..1ae5ceba7eb 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -32,8 +32,11 @@
#include <asm/current.h>
#include <asm/apicdef.h>
#include <asm/atomic.h>
+#include <asm/apicdef.h>
#include "kvm_cache_regs.h"
#include "irq.h"
+#include "trace.h"
+#include "x86.h"
#ifndef CONFIG_X86_64
#define mod_64(x, y) ((x) - (y) * div64_u64(x, y))
@@ -141,6 +144,26 @@ static inline int apic_lvt_nmi_mode(u32 lvt_val)
return (lvt_val & (APIC_MODE_MASK | APIC_LVT_MASKED)) == APIC_DM_NMI;
}
+void kvm_apic_set_version(struct kvm_vcpu *vcpu)
+{
+ struct kvm_lapic *apic = vcpu->arch.apic;
+ struct kvm_cpuid_entry2 *feat;
+ u32 v = APIC_VERSION;
+
+ if (!irqchip_in_kernel(vcpu->kvm))
+ return;
+
+ feat = kvm_find_cpuid_entry(apic->vcpu, 0x1, 0);
+ if (feat && (feat->ecx & (1 << (X86_FEATURE_X2APIC & 31))))
+ v |= APIC_LVR_DIRECTED_EOI;
+ apic_set_reg(apic, APIC_LVR, v);
+}
+
+static inline int apic_x2apic_mode(struct kvm_lapic *apic)
+{
+ return apic->vcpu->arch.apic_base & X2APIC_ENABLE;
+}
+
static unsigned int apic_lvt_mask[APIC_LVT_NUM] = {
LVT_MASK | APIC_LVT_TIMER_PERIODIC, /* LVTT */
LVT_MASK | APIC_MODE_MASK, /* LVTTHMR */
@@ -165,36 +188,52 @@ static int find_highest_vector(void *bitmap)
static inline int apic_test_and_set_irr(int vec, struct kvm_lapic *apic)
{
+ apic->irr_pending = true;
return apic_test_and_set_vector(vec, apic->regs + APIC_IRR);
}
-static inline void apic_clear_irr(int vec, struct kvm_lapic *apic)
+static inline int apic_search_irr(struct kvm_lapic *apic)
{
- apic_clear_vector(vec, apic->regs + APIC_IRR);
+ return find_highest_vector(apic->regs + APIC_IRR);
}
static inline int apic_find_highest_irr(struct kvm_lapic *apic)
{
int result;
- result = find_highest_vector(apic->regs + APIC_IRR);
+ if (!apic->irr_pending)
+ return -1;
+
+ result = apic_search_irr(apic);
ASSERT(result == -1 || result >= 16);
return result;
}
+static inline void apic_clear_irr(int vec, struct kvm_lapic *apic)
+{
+ apic->irr_pending = false;
+ apic_clear_vector(vec, apic->regs + APIC_IRR);
+ if (apic_search_irr(apic) != -1)
+ apic->irr_pending = true;
+}
+
int kvm_lapic_find_highest_irr(struct kvm_vcpu *vcpu)
{
struct kvm_lapic *apic = vcpu->arch.apic;
int highest_irr;
+ /* This may race with setting of irr in __apic_accept_irq() and
+ * value returned may be wrong, but kvm_vcpu_kick() in __apic_accept_irq
+ * will cause vmexit immediately and the value will be recalculated
+ * on the next vmentry.
+ */
if (!apic)
return 0;
highest_irr = apic_find_highest_irr(apic);
return highest_irr;
}
-EXPORT_SYMBOL_GPL(kvm_lapic_find_highest_irr);
static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
int vector, int level, int trig_mode);
@@ -251,7 +290,12 @@ int kvm_apic_match_physical_addr(struct kvm_lapic *apic, u16 dest)
int kvm_apic_match_logical_addr(struct kvm_lapic *apic, u8 mda)
{
int result = 0;
- u8 logical_id;
+ u32 logical_id;
+
+ if (apic_x2apic_mode(apic)) {
+ logical_id = apic_get_reg(apic, APIC_LDR);
+ return logical_id & mda;
+ }
logical_id = GET_APIC_LOGICAL_ID(apic_get_reg(apic, APIC_LDR));
@@ -331,6 +375,8 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
break;
result = !apic_test_and_set_irr(vector, apic);
+ trace_kvm_apic_accept_irq(vcpu->vcpu_id, delivery_mode,
+ trig_mode, vector, !result);
if (!result) {
if (trig_mode)
apic_debug("level trig mode repeatedly for "
@@ -425,7 +471,11 @@ static void apic_set_eoi(struct kvm_lapic *apic)
trigger_mode = IOAPIC_LEVEL_TRIG;
else
trigger_mode = IOAPIC_EDGE_TRIG;
- kvm_ioapic_update_eoi(apic->vcpu->kvm, vector, trigger_mode);
+ if (!(apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI)) {
+ mutex_lock(&apic->vcpu->kvm->irq_lock);
+ kvm_ioapic_update_eoi(apic->vcpu->kvm, vector, trigger_mode);
+ mutex_unlock(&apic->vcpu->kvm->irq_lock);
+ }
}
static void apic_send_ipi(struct kvm_lapic *apic)
@@ -440,7 +490,12 @@ static void apic_send_ipi(struct kvm_lapic *apic)
irq.level = icr_low & APIC_INT_ASSERT;
irq.trig_mode = icr_low & APIC_INT_LEVELTRIG;
irq.shorthand = icr_low & APIC_SHORT_MASK;
- irq.dest_id = GET_APIC_DEST_FIELD(icr_high);
+ if (apic_x2apic_mode(apic))
+ irq.dest_id = icr_high;
+ else
+ irq.dest_id = GET_APIC_DEST_FIELD(icr_high);
+
+ trace_kvm_apic_ipi(icr_low, irq.dest_id);
apic_debug("icr_high 0x%x, icr_low 0x%x, "
"short_hand 0x%x, dest 0x%x, trig_mode 0x%x, level 0x%x, "
@@ -449,7 +504,9 @@ static void apic_send_ipi(struct kvm_lapic *apic)
irq.trig_mode, irq.level, irq.dest_mode, irq.delivery_mode,
irq.vector);
+ mutex_lock(&apic->vcpu->kvm->irq_lock);
kvm_irq_delivery_to_apic(apic->vcpu->kvm, apic, &irq);
+ mutex_unlock(&apic->vcpu->kvm->irq_lock);
}
static u32 apic_get_tmcct(struct kvm_lapic *apic)
@@ -495,12 +552,16 @@ static u32 __apic_read(struct kvm_lapic *apic, unsigned int offset)
{
u32 val = 0;
- KVMTRACE_1D(APIC_ACCESS, apic->vcpu, (u32)offset, handler);
-
if (offset >= LAPIC_MMIO_LENGTH)
return 0;
switch (offset) {
+ case APIC_ID:
+ if (apic_x2apic_mode(apic))
+ val = kvm_apic_id(apic);
+ else
+ val = kvm_apic_id(apic) << 24;
+ break;
case APIC_ARBPRI:
printk(KERN_WARNING "Access APIC ARBPRI register "
"which is for P6\n");
@@ -522,21 +583,35 @@ static u32 __apic_read(struct kvm_lapic *apic, unsigned int offset)
return val;
}
-static void apic_mmio_read(struct kvm_io_device *this,
- gpa_t address, int len, void *data)
+static inline struct kvm_lapic *to_lapic(struct kvm_io_device *dev)
+{
+ return container_of(dev, struct kvm_lapic, dev);
+}
+
+static int apic_reg_read(struct kvm_lapic *apic, u32 offset, int len,
+ void *data)
{
- struct kvm_lapic *apic = (struct kvm_lapic *)this->private;
- unsigned int offset = address - apic->base_address;
unsigned char alignment = offset & 0xf;
u32 result;
+ /* this bitmask has a bit cleared for each reserver register */
+ static const u64 rmask = 0x43ff01ffffffe70cULL;
if ((alignment + len) > 4) {
- printk(KERN_ERR "KVM_APIC_READ: alignment error %lx %d",
- (unsigned long)address, len);
- return;
+ apic_debug("KVM_APIC_READ: alignment error %x %d\n",
+ offset, len);
+ return 1;
}
+
+ if (offset > 0x3f0 || !(rmask & (1ULL << (offset >> 4)))) {
+ apic_debug("KVM_APIC_READ: read reserved register %x\n",
+ offset);
+ return 1;
+ }
+
result = __apic_read(apic, offset & ~0xf);
+ trace_kvm_apic_read(offset, result);
+
switch (len) {
case 1:
case 2:
@@ -548,6 +623,28 @@ static void apic_mmio_read(struct kvm_io_device *this,
"should be 1,2, or 4 instead\n", len);
break;
}
+ return 0;
+}
+
+static int apic_mmio_in_range(struct kvm_lapic *apic, gpa_t addr)
+{
+ return apic_hw_enabled(apic) &&
+ addr >= apic->base_address &&
+ addr < apic->base_address + LAPIC_MMIO_LENGTH;
+}
+
+static int apic_mmio_read(struct kvm_io_device *this,
+ gpa_t address, int len, void *data)
+{
+ struct kvm_lapic *apic = to_lapic(this);
+ u32 offset = address - apic->base_address;
+
+ if (!apic_mmio_in_range(apic, address))
+ return -EOPNOTSUPP;
+
+ apic_reg_read(apic, offset, len, data);
+
+ return 0;
}
static void update_divide_count(struct kvm_lapic *apic)
@@ -573,6 +670,15 @@ static void start_apic_timer(struct kvm_lapic *apic)
if (!apic->lapic_timer.period)
return;
+ /*
+ * Do not allow the guest to program periodic timers with small
+ * interval, since the hrtimers are not throttled by the host
+ * scheduler.
+ */
+ if (apic_lvtt_period(apic)) {
+ if (apic->lapic_timer.period < NSEC_PER_MSEC/2)
+ apic->lapic_timer.period = NSEC_PER_MSEC/2;
+ }
hrtimer_start(&apic->lapic_timer.timer,
ktime_add_ns(now, apic->lapic_timer.period),
@@ -603,40 +709,18 @@ static void apic_manage_nmi_watchdog(struct kvm_lapic *apic, u32 lvt0_val)
apic->vcpu->kvm->arch.vapics_in_nmi_mode--;
}
-static void apic_mmio_write(struct kvm_io_device *this,
- gpa_t address, int len, const void *data)
+static int apic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val)
{
- struct kvm_lapic *apic = (struct kvm_lapic *)this->private;
- unsigned int offset = address - apic->base_address;
- unsigned char alignment = offset & 0xf;
- u32 val;
-
- /*
- * APIC register must be aligned on 128-bits boundary.
- * 32/64/128 bits registers must be accessed thru 32 bits.
- * Refer SDM 8.4.1
- */
- if (len != 4 || alignment) {
- /* Don't shout loud, $infamous_os would cause only noise. */
- apic_debug("apic write: bad size=%d %lx\n",
- len, (long)address);
- return;
- }
-
- val = *(u32 *) data;
-
- /* too common printing */
- if (offset != APIC_EOI)
- apic_debug("%s: offset 0x%x with length 0x%x, and value is "
- "0x%x\n", __func__, offset, len, val);
-
- offset &= 0xff0;
+ int ret = 0;
- KVMTRACE_1D(APIC_ACCESS, apic->vcpu, (u32)offset, handler);
+ trace_kvm_apic_write(reg, val);
- switch (offset) {
+ switch (reg) {
case APIC_ID: /* Local APIC ID */
- apic_set_reg(apic, APIC_ID, val);
+ if (!apic_x2apic_mode(apic))
+ apic_set_reg(apic, APIC_ID, val);
+ else
+ ret = 1;
break;
case APIC_TASKPRI:
@@ -649,15 +733,24 @@ static void apic_mmio_write(struct kvm_io_device *this,
break;
case APIC_LDR:
- apic_set_reg(apic, APIC_LDR, val & APIC_LDR_MASK);
+ if (!apic_x2apic_mode(apic))
+ apic_set_reg(apic, APIC_LDR, val & APIC_LDR_MASK);
+ else
+ ret = 1;
break;
case APIC_DFR:
- apic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF);
+ if (!apic_x2apic_mode(apic))
+ apic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF);
+ else
+ ret = 1;
break;
- case APIC_SPIV:
- apic_set_reg(apic, APIC_SPIV, val & 0x3ff);
+ case APIC_SPIV: {
+ u32 mask = 0x3ff;
+ if (apic_get_reg(apic, APIC_LVR) & APIC_LVR_DIRECTED_EOI)
+ mask |= APIC_SPIV_DIRECTED_EOI;
+ apic_set_reg(apic, APIC_SPIV, val & mask);
if (!(val & APIC_SPIV_APIC_ENABLED)) {
int i;
u32 lvt_val;
@@ -672,7 +765,7 @@ static void apic_mmio_write(struct kvm_io_device *this,
}
break;
-
+ }
case APIC_ICR:
/* No delay here, so we always clear the pending bit */
apic_set_reg(apic, APIC_ICR, val & ~(1 << 12));
@@ -680,7 +773,9 @@ static void apic_mmio_write(struct kvm_io_device *this,
break;
case APIC_ICR2:
- apic_set_reg(apic, APIC_ICR2, val & 0xff000000);
+ if (!apic_x2apic_mode(apic))
+ val &= 0xff000000;
+ apic_set_reg(apic, APIC_ICR2, val);
break;
case APIC_LVT0:
@@ -694,8 +789,8 @@ static void apic_mmio_write(struct kvm_io_device *this,
if (!apic_sw_enabled(apic))
val |= APIC_LVT_MASKED;
- val &= apic_lvt_mask[(offset - APIC_LVTT) >> 4];
- apic_set_reg(apic, offset, val);
+ val &= apic_lvt_mask[(reg - APIC_LVTT) >> 4];
+ apic_set_reg(apic, reg, val);
break;
@@ -703,7 +798,7 @@ static void apic_mmio_write(struct kvm_io_device *this,
hrtimer_cancel(&apic->lapic_timer.timer);
apic_set_reg(apic, APIC_TMICT, val);
start_apic_timer(apic);
- return;
+ break;
case APIC_TDCR:
if (val & 4)
@@ -712,27 +807,59 @@ static void apic_mmio_write(struct kvm_io_device *this,
update_divide_count(apic);
break;
+ case APIC_ESR:
+ if (apic_x2apic_mode(apic) && val != 0) {
+ printk(KERN_ERR "KVM_WRITE:ESR not zero %x\n", val);
+ ret = 1;
+ }
+ break;
+
+ case APIC_SELF_IPI:
+ if (apic_x2apic_mode(apic)) {
+ apic_reg_write(apic, APIC_ICR, 0x40000 | (val & 0xff));
+ } else
+ ret = 1;
+ break;
default:
- apic_debug("Local APIC Write to read-only register %x\n",
- offset);
+ ret = 1;
break;
}
-
+ if (ret)
+ apic_debug("Local APIC Write to read-only register %x\n", reg);
+ return ret;
}
-static int apic_mmio_range(struct kvm_io_device *this, gpa_t addr,
- int len, int size)
+static int apic_mmio_write(struct kvm_io_device *this,
+ gpa_t address, int len, const void *data)
{
- struct kvm_lapic *apic = (struct kvm_lapic *)this->private;
- int ret = 0;
+ struct kvm_lapic *apic = to_lapic(this);
+ unsigned int offset = address - apic->base_address;
+ u32 val;
+ if (!apic_mmio_in_range(apic, address))
+ return -EOPNOTSUPP;
- if (apic_hw_enabled(apic) &&
- (addr >= apic->base_address) &&
- (addr < (apic->base_address + LAPIC_MMIO_LENGTH)))
- ret = 1;
+ /*
+ * APIC register must be aligned on 128-bits boundary.
+ * 32/64/128 bits registers must be accessed thru 32 bits.
+ * Refer SDM 8.4.1
+ */
+ if (len != 4 || (offset & 0xf)) {
+ /* Don't shout loud, $infamous_os would cause only noise. */
+ apic_debug("apic write: bad size=%d %lx\n", len, (long)address);
+ return 0;
+ }
- return ret;
+ val = *(u32*)data;
+
+ /* too common printing */
+ if (offset != APIC_EOI)
+ apic_debug("%s: offset 0x%x with length 0x%x, and value is "
+ "0x%x\n", __func__, offset, len, val);
+
+ apic_reg_write(apic, offset & 0xff0, val);
+
+ return 0;
}
void kvm_free_lapic(struct kvm_vcpu *vcpu)
@@ -763,7 +890,6 @@ void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8)
apic_set_tpr(apic, ((cr8 & 0x0f) << 4)
| (apic_get_reg(apic, APIC_TASKPRI) & 4));
}
-EXPORT_SYMBOL_GPL(kvm_lapic_set_tpr);
u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu)
{
@@ -776,7 +902,6 @@ u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu)
return (tpr & 0xf0) >> 4;
}
-EXPORT_SYMBOL_GPL(kvm_lapic_get_cr8);
void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)
{
@@ -787,10 +912,16 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)
vcpu->arch.apic_base = value;
return;
}
- if (apic->vcpu->vcpu_id)
+
+ if (!kvm_vcpu_is_bsp(apic->vcpu))
value &= ~MSR_IA32_APICBASE_BSP;
vcpu->arch.apic_base = value;
+ if (apic_x2apic_mode(apic)) {
+ u32 id = kvm_apic_id(apic);
+ u32 ldr = ((id & ~0xf) << 16) | (1 << (id & 0xf));
+ apic_set_reg(apic, APIC_LDR, ldr);
+ }
apic->base_address = apic->vcpu->arch.apic_base &
MSR_IA32_APICBASE_BASE;
@@ -800,12 +931,6 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)
}
-u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu)
-{
- return vcpu->arch.apic_base;
-}
-EXPORT_SYMBOL_GPL(kvm_lapic_get_base);
-
void kvm_lapic_reset(struct kvm_vcpu *vcpu)
{
struct kvm_lapic *apic;
@@ -821,7 +946,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
hrtimer_cancel(&apic->lapic_timer.timer);
apic_set_reg(apic, APIC_ID, vcpu->vcpu_id << 24);
- apic_set_reg(apic, APIC_LVR, APIC_VERSION);
+ kvm_apic_set_version(apic->vcpu);
for (i = 0; i < APIC_LVT_NUM; i++)
apic_set_reg(apic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED);
@@ -842,9 +967,10 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
apic_set_reg(apic, APIC_ISR + 0x10 * i, 0);
apic_set_reg(apic, APIC_TMR + 0x10 * i, 0);
}
+ apic->irr_pending = false;
update_divide_count(apic);
atomic_set(&apic->lapic_timer.pending, 0);
- if (vcpu->vcpu_id == 0)
+ if (kvm_vcpu_is_bsp(vcpu))
vcpu->arch.apic_base |= MSR_IA32_APICBASE_BSP;
apic_update_ppr(apic);
@@ -855,7 +981,6 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
vcpu, kvm_apic_id(apic),
vcpu->arch.apic_base, apic->base_address);
}
-EXPORT_SYMBOL_GPL(kvm_lapic_reset);
bool kvm_apic_present(struct kvm_vcpu *vcpu)
{
@@ -866,7 +991,6 @@ int kvm_lapic_enabled(struct kvm_vcpu *vcpu)
{
return kvm_apic_present(vcpu) && apic_sw_enabled(vcpu->arch.apic);
}
-EXPORT_SYMBOL_GPL(kvm_lapic_enabled);
/*
*----------------------------------------------------------------------
@@ -917,6 +1041,11 @@ static struct kvm_timer_ops lapic_timer_ops = {
.is_periodic = lapic_is_periodic,
};
+static const struct kvm_io_device_ops apic_mmio_ops = {
+ .read = apic_mmio_read,
+ .write = apic_mmio_write,
+};
+
int kvm_create_lapic(struct kvm_vcpu *vcpu)
{
struct kvm_lapic *apic;
@@ -945,16 +1074,13 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu)
apic->lapic_timer.timer.function = kvm_timer_fn;
apic->lapic_timer.t_ops = &lapic_timer_ops;
apic->lapic_timer.kvm = vcpu->kvm;
- apic->lapic_timer.vcpu_id = vcpu->vcpu_id;
+ apic->lapic_timer.vcpu = vcpu;
apic->base_address = APIC_DEFAULT_PHYS_BASE;
vcpu->arch.apic_base = APIC_DEFAULT_PHYS_BASE;
kvm_lapic_reset(vcpu);
- apic->dev.read = apic_mmio_read;
- apic->dev.write = apic_mmio_write;
- apic->dev.in_range = apic_mmio_range;
- apic->dev.private = apic;
+ kvm_iodevice_init(&apic->dev, &apic_mmio_ops);
return 0;
nomem_free_apic:
@@ -962,7 +1088,6 @@ nomem_free_apic:
nomem:
return -ENOMEM;
}
-EXPORT_SYMBOL_GPL(kvm_create_lapic);
int kvm_apic_has_interrupt(struct kvm_vcpu *vcpu)
{
@@ -985,7 +1110,7 @@ int kvm_apic_accept_pic_intr(struct kvm_vcpu *vcpu)
u32 lvt0 = apic_get_reg(vcpu->arch.apic, APIC_LVT0);
int r = 0;
- if (vcpu->vcpu_id == 0) {
+ if (kvm_vcpu_is_bsp(vcpu)) {
if (!apic_hw_enabled(vcpu->arch.apic))
r = 1;
if ((lvt0 & APIC_LVT_MASKED) == 0 &&
@@ -1025,7 +1150,8 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu)
apic->base_address = vcpu->arch.apic_base &
MSR_IA32_APICBASE_BASE;
- apic_set_reg(apic, APIC_LVR, APIC_VERSION);
+ kvm_apic_set_version(vcpu);
+
apic_update_ppr(apic);
hrtimer_cancel(&apic->lapic_timer.timer);
update_divide_count(apic);
@@ -1092,3 +1218,35 @@ void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr)
vcpu->arch.apic->vapic_addr = vapic_addr;
}
+
+int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data)
+{
+ struct kvm_lapic *apic = vcpu->arch.apic;
+ u32 reg = (msr - APIC_BASE_MSR) << 4;
+
+ if (!irqchip_in_kernel(vcpu->kvm) || !apic_x2apic_mode(apic))
+ return 1;
+
+ /* if this is ICR write vector before command */
+ if (msr == 0x830)
+ apic_reg_write(apic, APIC_ICR2, (u32)(data >> 32));
+ return apic_reg_write(apic, reg, (u32)data);
+}
+
+int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data)
+{
+ struct kvm_lapic *apic = vcpu->arch.apic;
+ u32 reg = (msr - APIC_BASE_MSR) << 4, low, high = 0;
+
+ if (!irqchip_in_kernel(vcpu->kvm) || !apic_x2apic_mode(apic))
+ return 1;
+
+ if (apic_reg_read(apic, reg, 4, &low))
+ return 1;
+ if (msr == 0x830)
+ apic_reg_read(apic, APIC_ICR2, 4, &high);
+
+ *data = (((u64)high) << 32) | low;
+
+ return 0;
+}
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index a587f8349c4..40010b09c4a 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -12,6 +12,7 @@ struct kvm_lapic {
struct kvm_timer lapic_timer;
u32 divide_count;
struct kvm_vcpu *vcpu;
+ bool irr_pending;
struct page *regs_page;
void *regs;
gpa_t vapic_addr;
@@ -28,6 +29,7 @@ u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu);
void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8);
void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value);
u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu);
+void kvm_apic_set_version(struct kvm_vcpu *vcpu);
int kvm_apic_match_physical_addr(struct kvm_lapic *apic, u16 dest);
int kvm_apic_match_logical_addr(struct kvm_lapic *apic, u8 mda);
@@ -44,4 +46,6 @@ void kvm_lapic_set_vapic_addr(struct kvm_vcpu *vcpu, gpa_t vapic_addr);
void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu);
void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu);
+int kvm_x2apic_msr_write(struct kvm_vcpu *vcpu, u32 msr, u64 data);
+int kvm_x2apic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data);
#endif
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index 0ef5bb2b404..eca41ae9f45 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -18,6 +18,7 @@
*/
#include "mmu.h"
+#include "kvm_cache_regs.h"
#include <linux/kvm_host.h>
#include <linux/types.h>
@@ -107,6 +108,9 @@ module_param(oos_shadow, bool, 0644);
#define PT32_LEVEL_MASK(level) \
(((1ULL << PT32_LEVEL_BITS) - 1) << PT32_LEVEL_SHIFT(level))
+#define PT32_LVL_OFFSET_MASK(level) \
+ (PT32_BASE_ADDR_MASK & ((1ULL << (PAGE_SHIFT + (((level) - 1) \
+ * PT32_LEVEL_BITS))) - 1))
#define PT32_INDEX(address, level)\
(((address) >> PT32_LEVEL_SHIFT(level)) & ((1 << PT32_LEVEL_BITS) - 1))
@@ -115,10 +119,19 @@ module_param(oos_shadow, bool, 0644);
#define PT64_BASE_ADDR_MASK (((1ULL << 52) - 1) & ~(u64)(PAGE_SIZE-1))
#define PT64_DIR_BASE_ADDR_MASK \
(PT64_BASE_ADDR_MASK & ~((1ULL << (PAGE_SHIFT + PT64_LEVEL_BITS)) - 1))
+#define PT64_LVL_ADDR_MASK(level) \
+ (PT64_BASE_ADDR_MASK & ~((1ULL << (PAGE_SHIFT + (((level) - 1) \
+ * PT64_LEVEL_BITS))) - 1))
+#define PT64_LVL_OFFSET_MASK(level) \
+ (PT64_BASE_ADDR_MASK & ((1ULL << (PAGE_SHIFT + (((level) - 1) \
+ * PT64_LEVEL_BITS))) - 1))
#define PT32_BASE_ADDR_MASK PAGE_MASK
#define PT32_DIR_BASE_ADDR_MASK \
(PAGE_MASK & ~((1ULL << (PAGE_SHIFT + PT32_LEVEL_BITS)) - 1))
+#define PT32_LVL_ADDR_MASK(level) \
+ (PAGE_MASK & ~((1ULL << (PAGE_SHIFT + (((level) - 1) \
+ * PT32_LEVEL_BITS))) - 1))
#define PT64_PERM_MASK (PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK \
| PT64_NX_MASK)
@@ -129,6 +142,7 @@ module_param(oos_shadow, bool, 0644);
#define PFERR_RSVD_MASK (1U << 3)
#define PFERR_FETCH_MASK (1U << 4)
+#define PT_PDPE_LEVEL 3
#define PT_DIRECTORY_LEVEL 2
#define PT_PAGE_TABLE_LEVEL 1
@@ -139,10 +153,13 @@ module_param(oos_shadow, bool, 0644);
#define ACC_USER_MASK PT_USER_MASK
#define ACC_ALL (ACC_EXEC_MASK | ACC_WRITE_MASK | ACC_USER_MASK)
+#define CREATE_TRACE_POINTS
+#include "mmutrace.h"
+
#define SHADOW_PT_INDEX(addr, level) PT64_INDEX(addr, level)
struct kvm_rmap_desc {
- u64 *shadow_ptes[RMAP_EXT];
+ u64 *sptes[RMAP_EXT];
struct kvm_rmap_desc *more;
};
@@ -239,16 +256,25 @@ static int is_writeble_pte(unsigned long pte)
return pte & PT_WRITABLE_MASK;
}
-static int is_dirty_pte(unsigned long pte)
+static int is_dirty_gpte(unsigned long pte)
{
- return pte & shadow_dirty_mask;
+ return pte & PT_DIRTY_MASK;
}
-static int is_rmap_pte(u64 pte)
+static int is_rmap_spte(u64 pte)
{
return is_shadow_present_pte(pte);
}
+static int is_last_spte(u64 pte, int level)
+{
+ if (level == PT_PAGE_TABLE_LEVEL)
+ return 1;
+ if (is_large_pte(pte))
+ return 1;
+ return 0;
+}
+
static pfn_t spte_to_pfn(u64 pte)
{
return (pte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT;
@@ -261,7 +287,7 @@ static gfn_t pse36_gfn_delta(u32 gpte)
return (gpte & PT32_DIR_PSE36_MASK) << shift;
}
-static void set_shadow_pte(u64 *sptep, u64 spte)
+static void __set_spte(u64 *sptep, u64 spte)
{
#ifdef CONFIG_X86_64
set_64bit((unsigned long *)sptep, spte);
@@ -380,37 +406,52 @@ static void mmu_free_rmap_desc(struct kvm_rmap_desc *rd)
* Return the pointer to the largepage write count for a given
* gfn, handling slots that are not large page aligned.
*/
-static int *slot_largepage_idx(gfn_t gfn, struct kvm_memory_slot *slot)
+static int *slot_largepage_idx(gfn_t gfn,
+ struct kvm_memory_slot *slot,
+ int level)
{
unsigned long idx;
- idx = (gfn / KVM_PAGES_PER_HPAGE) -
- (slot->base_gfn / KVM_PAGES_PER_HPAGE);
- return &slot->lpage_info[idx].write_count;
+ idx = (gfn / KVM_PAGES_PER_HPAGE(level)) -
+ (slot->base_gfn / KVM_PAGES_PER_HPAGE(level));
+ return &slot->lpage_info[level - 2][idx].write_count;
}
static void account_shadowed(struct kvm *kvm, gfn_t gfn)
{
+ struct kvm_memory_slot *slot;
int *write_count;
+ int i;
gfn = unalias_gfn(kvm, gfn);
- write_count = slot_largepage_idx(gfn,
- gfn_to_memslot_unaliased(kvm, gfn));
- *write_count += 1;
+
+ slot = gfn_to_memslot_unaliased(kvm, gfn);
+ for (i = PT_DIRECTORY_LEVEL;
+ i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) {
+ write_count = slot_largepage_idx(gfn, slot, i);
+ *write_count += 1;
+ }
}
static void unaccount_shadowed(struct kvm *kvm, gfn_t gfn)
{
+ struct kvm_memory_slot *slot;
int *write_count;
+ int i;
gfn = unalias_gfn(kvm, gfn);
- write_count = slot_largepage_idx(gfn,
- gfn_to_memslot_unaliased(kvm, gfn));
- *write_count -= 1;
- WARN_ON(*write_count < 0);
+ for (i = PT_DIRECTORY_LEVEL;
+ i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) {
+ slot = gfn_to_memslot_unaliased(kvm, gfn);
+ write_count = slot_largepage_idx(gfn, slot, i);
+ *write_count -= 1;
+ WARN_ON(*write_count < 0);
+ }
}
-static int has_wrprotected_page(struct kvm *kvm, gfn_t gfn)
+static int has_wrprotected_page(struct kvm *kvm,
+ gfn_t gfn,
+ int level)
{
struct kvm_memory_slot *slot;
int *largepage_idx;
@@ -418,47 +459,67 @@ static int has_wrprotected_page(struct kvm *kvm, gfn_t gfn)
gfn = unalias_gfn(kvm, gfn);
slot = gfn_to_memslot_unaliased(kvm, gfn);
if (slot) {
- largepage_idx = slot_largepage_idx(gfn, slot);
+ largepage_idx = slot_largepage_idx(gfn, slot, level);
return *largepage_idx;
}
return 1;
}
-static int host_largepage_backed(struct kvm *kvm, gfn_t gfn)
+static int host_mapping_level(struct kvm *kvm, gfn_t gfn)
{
+ unsigned long page_size = PAGE_SIZE;
struct vm_area_struct *vma;
unsigned long addr;
- int ret = 0;
+ int i, ret = 0;
addr = gfn_to_hva(kvm, gfn);
if (kvm_is_error_hva(addr))
- return ret;
+ return page_size;
down_read(&current->mm->mmap_sem);
vma = find_vma(current->mm, addr);
- if (vma && is_vm_hugetlb_page(vma))
- ret = 1;
+ if (!vma)
+ goto out;
+
+ page_size = vma_kernel_pagesize(vma);
+
+out:
up_read(&current->mm->mmap_sem);
+ for (i = PT_PAGE_TABLE_LEVEL;
+ i < (PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES); ++i) {
+ if (page_size >= KVM_HPAGE_SIZE(i))
+ ret = i;
+ else
+ break;
+ }
+
return ret;
}
-static int is_largepage_backed(struct kvm_vcpu *vcpu, gfn_t large_gfn)
+static int mapping_level(struct kvm_vcpu *vcpu, gfn_t large_gfn)
{
struct kvm_memory_slot *slot;
-
- if (has_wrprotected_page(vcpu->kvm, large_gfn))
- return 0;
-
- if (!host_largepage_backed(vcpu->kvm, large_gfn))
- return 0;
+ int host_level;
+ int level = PT_PAGE_TABLE_LEVEL;
slot = gfn_to_memslot(vcpu->kvm, large_gfn);
if (slot && slot->dirty_bitmap)
- return 0;
+ return PT_PAGE_TABLE_LEVEL;
- return 1;
+ host_level = host_mapping_level(vcpu->kvm, large_gfn);
+
+ if (host_level == PT_PAGE_TABLE_LEVEL)
+ return host_level;
+
+ for (level = PT_DIRECTORY_LEVEL; level <= host_level; ++level) {
+
+ if (has_wrprotected_page(vcpu->kvm, large_gfn, level))
+ break;
+ }
+
+ return level - 1;
}
/*
@@ -466,19 +527,19 @@ static int is_largepage_backed(struct kvm_vcpu *vcpu, gfn_t large_gfn)
* Note: gfn must be unaliased before this function get called
*/
-static unsigned long *gfn_to_rmap(struct kvm *kvm, gfn_t gfn, int lpage)
+static unsigned long *gfn_to_rmap(struct kvm *kvm, gfn_t gfn, int level)
{
struct kvm_memory_slot *slot;
unsigned long idx;
slot = gfn_to_memslot(kvm, gfn);
- if (!lpage)
+ if (likely(level == PT_PAGE_TABLE_LEVEL))
return &slot->rmap[gfn - slot->base_gfn];
- idx = (gfn / KVM_PAGES_PER_HPAGE) -
- (slot->base_gfn / KVM_PAGES_PER_HPAGE);
+ idx = (gfn / KVM_PAGES_PER_HPAGE(level)) -
+ (slot->base_gfn / KVM_PAGES_PER_HPAGE(level));
- return &slot->lpage_info[idx].rmap_pde;
+ return &slot->lpage_info[level - 2][idx].rmap_pde;
}
/*
@@ -494,42 +555,42 @@ static unsigned long *gfn_to_rmap(struct kvm *kvm, gfn_t gfn, int lpage)
* the spte was not added.
*
*/
-static int rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn, int lpage)
+static int rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn)
{
struct kvm_mmu_page *sp;
struct kvm_rmap_desc *desc;
unsigned long *rmapp;
int i, count = 0;
- if (!is_rmap_pte(*spte))
+ if (!is_rmap_spte(*spte))
return count;
gfn = unalias_gfn(vcpu->kvm, gfn);
sp = page_header(__pa(spte));
sp->gfns[spte - sp->spt] = gfn;
- rmapp = gfn_to_rmap(vcpu->kvm, gfn, lpage);
+ rmapp = gfn_to_rmap(vcpu->kvm, gfn, sp->role.level);
if (!*rmapp) {
rmap_printk("rmap_add: %p %llx 0->1\n", spte, *spte);
*rmapp = (unsigned long)spte;
} else if (!(*rmapp & 1)) {
rmap_printk("rmap_add: %p %llx 1->many\n", spte, *spte);
desc = mmu_alloc_rmap_desc(vcpu);
- desc->shadow_ptes[0] = (u64 *)*rmapp;
- desc->shadow_ptes[1] = spte;
+ desc->sptes[0] = (u64 *)*rmapp;
+ desc->sptes[1] = spte;
*rmapp = (unsigned long)desc | 1;
} else {
rmap_printk("rmap_add: %p %llx many->many\n", spte, *spte);
desc = (struct kvm_rmap_desc *)(*rmapp & ~1ul);
- while (desc->shadow_ptes[RMAP_EXT-1] && desc->more) {
+ while (desc->sptes[RMAP_EXT-1] && desc->more) {
desc = desc->more;
count += RMAP_EXT;
}
- if (desc->shadow_ptes[RMAP_EXT-1]) {
+ if (desc->sptes[RMAP_EXT-1]) {
desc->more = mmu_alloc_rmap_desc(vcpu);
desc = desc->more;
}
- for (i = 0; desc->shadow_ptes[i]; ++i)
+ for (i = 0; desc->sptes[i]; ++i)
;
- desc->shadow_ptes[i] = spte;
+ desc->sptes[i] = spte;
}
return count;
}
@@ -541,14 +602,14 @@ static void rmap_desc_remove_entry(unsigned long *rmapp,
{
int j;
- for (j = RMAP_EXT - 1; !desc->shadow_ptes[j] && j > i; --j)
+ for (j = RMAP_EXT - 1; !desc->sptes[j] && j > i; --j)
;
- desc->shadow_ptes[i] = desc->shadow_ptes[j];
- desc->shadow_ptes[j] = NULL;
+ desc->sptes[i] = desc->sptes[j];
+ desc->sptes[j] = NULL;
if (j != 0)
return;
if (!prev_desc && !desc->more)
- *rmapp = (unsigned long)desc->shadow_ptes[0];
+ *rmapp = (unsigned long)desc->sptes[0];
else
if (prev_desc)
prev_desc->more = desc->more;
@@ -566,7 +627,7 @@ static void rmap_remove(struct kvm *kvm, u64 *spte)
unsigned long *rmapp;
int i;
- if (!is_rmap_pte(*spte))
+ if (!is_rmap_spte(*spte))
return;
sp = page_header(__pa(spte));
pfn = spte_to_pfn(*spte);
@@ -576,7 +637,7 @@ static void rmap_remove(struct kvm *kvm, u64 *spte)
kvm_release_pfn_dirty(pfn);
else
kvm_release_pfn_clean(pfn);
- rmapp = gfn_to_rmap(kvm, sp->gfns[spte - sp->spt], is_large_pte(*spte));
+ rmapp = gfn_to_rmap(kvm, sp->gfns[spte - sp->spt], sp->role.level);
if (!*rmapp) {
printk(KERN_ERR "rmap_remove: %p %llx 0->BUG\n", spte, *spte);
BUG();
@@ -593,8 +654,8 @@ static void rmap_remove(struct kvm *kvm, u64 *spte)
desc = (struct kvm_rmap_desc *)(*rmapp & ~1ul);
prev_desc = NULL;
while (desc) {
- for (i = 0; i < RMAP_EXT && desc->shadow_ptes[i]; ++i)
- if (desc->shadow_ptes[i] == spte) {
+ for (i = 0; i < RMAP_EXT && desc->sptes[i]; ++i)
+ if (desc->sptes[i] == spte) {
rmap_desc_remove_entry(rmapp,
desc, i,
prev_desc);
@@ -625,10 +686,10 @@ static u64 *rmap_next(struct kvm *kvm, unsigned long *rmapp, u64 *spte)
prev_desc = NULL;
prev_spte = NULL;
while (desc) {
- for (i = 0; i < RMAP_EXT && desc->shadow_ptes[i]; ++i) {
+ for (i = 0; i < RMAP_EXT && desc->sptes[i]; ++i) {
if (prev_spte == spte)
- return desc->shadow_ptes[i];
- prev_spte = desc->shadow_ptes[i];
+ return desc->sptes[i];
+ prev_spte = desc->sptes[i];
}
desc = desc->more;
}
@@ -639,10 +700,10 @@ static int rmap_write_protect(struct kvm *kvm, u64 gfn)
{
unsigned long *rmapp;
u64 *spte;
- int write_protected = 0;
+ int i, write_protected = 0;
gfn = unalias_gfn(kvm, gfn);
- rmapp = gfn_to_rmap(kvm, gfn, 0);
+ rmapp = gfn_to_rmap(kvm, gfn, PT_PAGE_TABLE_LEVEL);
spte = rmap_next(kvm, rmapp, NULL);
while (spte) {
@@ -650,7 +711,7 @@ static int rmap_write_protect(struct kvm *kvm, u64 gfn)
BUG_ON(!(*spte & PT_PRESENT_MASK));
rmap_printk("rmap_write_protect: spte %p %llx\n", spte, *spte);
if (is_writeble_pte(*spte)) {
- set_shadow_pte(spte, *spte & ~PT_WRITABLE_MASK);
+ __set_spte(spte, *spte & ~PT_WRITABLE_MASK);
write_protected = 1;
}
spte = rmap_next(kvm, rmapp, spte);
@@ -664,21 +725,24 @@ static int rmap_write_protect(struct kvm *kvm, u64 gfn)
}
/* check for huge page mappings */
- rmapp = gfn_to_rmap(kvm, gfn, 1);
- spte = rmap_next(kvm, rmapp, NULL);
- while (spte) {
- BUG_ON(!spte);
- BUG_ON(!(*spte & PT_PRESENT_MASK));
- BUG_ON((*spte & (PT_PAGE_SIZE_MASK|PT_PRESENT_MASK)) != (PT_PAGE_SIZE_MASK|PT_PRESENT_MASK));
- pgprintk("rmap_write_protect(large): spte %p %llx %lld\n", spte, *spte, gfn);
- if (is_writeble_pte(*spte)) {
- rmap_remove(kvm, spte);
- --kvm->stat.lpages;
- set_shadow_pte(spte, shadow_trap_nonpresent_pte);
- spte = NULL;
- write_protected = 1;
+ for (i = PT_DIRECTORY_LEVEL;
+ i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) {
+ rmapp = gfn_to_rmap(kvm, gfn, i);
+ spte = rmap_next(kvm, rmapp, NULL);
+ while (spte) {
+ BUG_ON(!spte);
+ BUG_ON(!(*spte & PT_PRESENT_MASK));
+ BUG_ON((*spte & (PT_PAGE_SIZE_MASK|PT_PRESENT_MASK)) != (PT_PAGE_SIZE_MASK|PT_PRESENT_MASK));
+ pgprintk("rmap_write_protect(large): spte %p %llx %lld\n", spte, *spte, gfn);
+ if (is_writeble_pte(*spte)) {
+ rmap_remove(kvm, spte);
+ --kvm->stat.lpages;
+ __set_spte(spte, shadow_trap_nonpresent_pte);
+ spte = NULL;
+ write_protected = 1;
+ }
+ spte = rmap_next(kvm, rmapp, spte);
}
- spte = rmap_next(kvm, rmapp, spte);
}
return write_protected;
@@ -693,7 +757,7 @@ static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp)
BUG_ON(!(*spte & PT_PRESENT_MASK));
rmap_printk("kvm_rmap_unmap_hva: spte %p %llx\n", spte, *spte);
rmap_remove(kvm, spte);
- set_shadow_pte(spte, shadow_trap_nonpresent_pte);
+ __set_spte(spte, shadow_trap_nonpresent_pte);
need_tlb_flush = 1;
}
return need_tlb_flush;
@@ -702,7 +766,7 @@ static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp)
static int kvm_handle_hva(struct kvm *kvm, unsigned long hva,
int (*handler)(struct kvm *kvm, unsigned long *rmapp))
{
- int i;
+ int i, j;
int retval = 0;
/*
@@ -721,11 +785,15 @@ static int kvm_handle_hva(struct kvm *kvm, unsigned long hva,
end = start + (memslot->npages << PAGE_SHIFT);
if (hva >= start && hva < end) {
gfn_t gfn_offset = (hva - start) >> PAGE_SHIFT;
+
retval |= handler(kvm, &memslot->rmap[gfn_offset]);
- retval |= handler(kvm,
- &memslot->lpage_info[
- gfn_offset /
- KVM_PAGES_PER_HPAGE].rmap_pde);
+
+ for (j = 0; j < KVM_NR_PAGE_SIZES - 1; ++j) {
+ int idx = gfn_offset;
+ idx /= KVM_PAGES_PER_HPAGE(PT_DIRECTORY_LEVEL + j);
+ retval |= handler(kvm,
+ &memslot->lpage_info[j][idx].rmap_pde);
+ }
}
}
@@ -763,12 +831,15 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp)
#define RMAP_RECYCLE_THRESHOLD 1000
-static void rmap_recycle(struct kvm_vcpu *vcpu, gfn_t gfn, int lpage)
+static void rmap_recycle(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn)
{
unsigned long *rmapp;
+ struct kvm_mmu_page *sp;
+
+ sp = page_header(__pa(spte));
gfn = unalias_gfn(vcpu->kvm, gfn);
- rmapp = gfn_to_rmap(vcpu->kvm, gfn, lpage);
+ rmapp = gfn_to_rmap(vcpu->kvm, gfn, sp->role.level);
kvm_unmap_rmapp(vcpu->kvm, rmapp);
kvm_flush_remote_tlbs(vcpu->kvm);
@@ -1109,6 +1180,7 @@ static int kvm_sync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
return 1;
}
+ trace_kvm_mmu_sync_page(sp);
if (rmap_write_protect(vcpu->kvm, sp->gfn))
kvm_flush_remote_tlbs(vcpu->kvm);
kvm_unlink_unsync_page(vcpu->kvm, sp);
@@ -1231,8 +1303,6 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
quadrant &= (1 << ((PT32_PT_BITS - PT64_PT_BITS) * level)) - 1;
role.quadrant = quadrant;
}
- pgprintk("%s: looking gfn %lx role %x\n", __func__,
- gfn, role.word);
index = kvm_page_table_hashfn(gfn);
bucket = &vcpu->kvm->arch.mmu_page_hash[index];
hlist_for_each_entry_safe(sp, node, tmp, bucket, hash_link)
@@ -1249,14 +1319,13 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
set_bit(KVM_REQ_MMU_SYNC, &vcpu->requests);
kvm_mmu_mark_parents_unsync(vcpu, sp);
}
- pgprintk("%s: found\n", __func__);
+ trace_kvm_mmu_get_page(sp, false);
return sp;
}
++vcpu->kvm->stat.mmu_cache_miss;
sp = kvm_mmu_alloc_page(vcpu, parent_pte);
if (!sp)
return sp;
- pgprintk("%s: adding gfn %lx role %x\n", __func__, gfn, role.word);
sp->gfn = gfn;
sp->role = role;
hlist_add_head(&sp->hash_link, bucket);
@@ -1269,6 +1338,7 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
vcpu->arch.mmu.prefetch_page(vcpu, sp);
else
nonpaging_prefetch_page(vcpu, sp);
+ trace_kvm_mmu_get_page(sp, true);
return sp;
}
@@ -1292,6 +1362,11 @@ static bool shadow_walk_okay(struct kvm_shadow_walk_iterator *iterator)
{
if (iterator->level < PT_PAGE_TABLE_LEVEL)
return false;
+
+ if (iterator->level == PT_PAGE_TABLE_LEVEL)
+ if (is_large_pte(*iterator->sptep))
+ return false;
+
iterator->index = SHADOW_PT_INDEX(iterator->addr, iterator->level);
iterator->sptep = ((u64 *)__va(iterator->shadow_addr)) + iterator->index;
return true;
@@ -1312,25 +1387,17 @@ static void kvm_mmu_page_unlink_children(struct kvm *kvm,
pt = sp->spt;
- if (sp->role.level == PT_PAGE_TABLE_LEVEL) {
- for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
- if (is_shadow_present_pte(pt[i]))
- rmap_remove(kvm, &pt[i]);
- pt[i] = shadow_trap_nonpresent_pte;
- }
- return;
- }
-
for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
ent = pt[i];
if (is_shadow_present_pte(ent)) {
- if (!is_large_pte(ent)) {
+ if (!is_last_spte(ent, sp->role.level)) {
ent &= PT64_BASE_ADDR_MASK;
mmu_page_remove_parent_pte(page_header(ent),
&pt[i]);
} else {
- --kvm->stat.lpages;
+ if (is_large_pte(ent))
+ --kvm->stat.lpages;
rmap_remove(kvm, &pt[i]);
}
}
@@ -1346,10 +1413,10 @@ static void kvm_mmu_put_page(struct kvm_mmu_page *sp, u64 *parent_pte)
static void kvm_mmu_reset_last_pte_updated(struct kvm *kvm)
{
int i;
+ struct kvm_vcpu *vcpu;
- for (i = 0; i < KVM_MAX_VCPUS; ++i)
- if (kvm->vcpus[i])
- kvm->vcpus[i]->arch.last_pte_updated = NULL;
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ vcpu->arch.last_pte_updated = NULL;
}
static void kvm_mmu_unlink_parents(struct kvm *kvm, struct kvm_mmu_page *sp)
@@ -1368,7 +1435,7 @@ static void kvm_mmu_unlink_parents(struct kvm *kvm, struct kvm_mmu_page *sp)
}
BUG_ON(!parent_pte);
kvm_mmu_put_page(sp, parent_pte);
- set_shadow_pte(parent_pte, shadow_trap_nonpresent_pte);
+ __set_spte(parent_pte, shadow_trap_nonpresent_pte);
}
}
@@ -1400,6 +1467,8 @@ static int mmu_zap_unsync_children(struct kvm *kvm,
static int kvm_mmu_zap_page(struct kvm *kvm, struct kvm_mmu_page *sp)
{
int ret;
+
+ trace_kvm_mmu_zap_page(sp);
++kvm->stat.mmu_shadow_zapped;
ret = mmu_zap_unsync_children(kvm, sp);
kvm_mmu_page_unlink_children(kvm, sp);
@@ -1516,7 +1585,7 @@ static void mmu_convert_notrap(struct kvm_mmu_page *sp)
for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
if (pt[i] == shadow_notrap_nonpresent_pte)
- set_shadow_pte(&pt[i], shadow_trap_nonpresent_pte);
+ __set_spte(&pt[i], shadow_trap_nonpresent_pte);
}
}
@@ -1646,6 +1715,7 @@ static int kvm_unsync_page(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
struct kvm_mmu_page *s;
struct hlist_node *node, *n;
+ trace_kvm_mmu_unsync_page(sp);
index = kvm_page_table_hashfn(sp->gfn);
bucket = &vcpu->kvm->arch.mmu_page_hash[index];
/* don't unsync if pagetable is shadowed with multiple roles */
@@ -1682,9 +1752,9 @@ static int mmu_need_write_protect(struct kvm_vcpu *vcpu, gfn_t gfn,
return 0;
}
-static int set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
+static int set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
unsigned pte_access, int user_fault,
- int write_fault, int dirty, int largepage,
+ int write_fault, int dirty, int level,
gfn_t gfn, pfn_t pfn, bool speculative,
bool can_unsync)
{
@@ -1707,7 +1777,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
spte |= shadow_nx_mask;
if (pte_access & ACC_USER_MASK)
spte |= shadow_user_mask;
- if (largepage)
+ if (level > PT_PAGE_TABLE_LEVEL)
spte |= PT_PAGE_SIZE_MASK;
if (tdp_enabled)
spte |= kvm_x86_ops->get_mt_mask(vcpu, gfn,
@@ -1718,7 +1788,8 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
if ((pte_access & ACC_WRITE_MASK)
|| (write_fault && !is_write_protection(vcpu) && !user_fault)) {
- if (largepage && has_wrprotected_page(vcpu->kvm, gfn)) {
+ if (level > PT_PAGE_TABLE_LEVEL &&
+ has_wrprotected_page(vcpu->kvm, gfn, level)) {
ret = 1;
spte = shadow_trap_nonpresent_pte;
goto set_pte;
@@ -1732,7 +1803,7 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
* is responsibility of mmu_get_page / kvm_sync_page.
* Same reasoning can be applied to dirty page accounting.
*/
- if (!can_unsync && is_writeble_pte(*shadow_pte))
+ if (!can_unsync && is_writeble_pte(*sptep))
goto set_pte;
if (mmu_need_write_protect(vcpu, gfn, can_unsync)) {
@@ -1749,65 +1820,67 @@ static int set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
mark_page_dirty(vcpu->kvm, gfn);
set_pte:
- set_shadow_pte(shadow_pte, spte);
+ __set_spte(sptep, spte);
return ret;
}
-static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
+static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *sptep,
unsigned pt_access, unsigned pte_access,
int user_fault, int write_fault, int dirty,
- int *ptwrite, int largepage, gfn_t gfn,
+ int *ptwrite, int level, gfn_t gfn,
pfn_t pfn, bool speculative)
{
int was_rmapped = 0;
- int was_writeble = is_writeble_pte(*shadow_pte);
+ int was_writeble = is_writeble_pte(*sptep);
int rmap_count;
pgprintk("%s: spte %llx access %x write_fault %d"
" user_fault %d gfn %lx\n",
- __func__, *shadow_pte, pt_access,
+ __func__, *sptep, pt_access,
write_fault, user_fault, gfn);
- if (is_rmap_pte(*shadow_pte)) {
+ if (is_rmap_spte(*sptep)) {
/*
* If we overwrite a PTE page pointer with a 2MB PMD, unlink
* the parent of the now unreachable PTE.
*/
- if (largepage && !is_large_pte(*shadow_pte)) {
+ if (level > PT_PAGE_TABLE_LEVEL &&
+ !is_large_pte(*sptep)) {
struct kvm_mmu_page *child;
- u64 pte = *shadow_pte;
+ u64 pte = *sptep;
child = page_header(pte & PT64_BASE_ADDR_MASK);
- mmu_page_remove_parent_pte(child, shadow_pte);
- } else if (pfn != spte_to_pfn(*shadow_pte)) {
+ mmu_page_remove_parent_pte(child, sptep);
+ } else if (pfn != spte_to_pfn(*sptep)) {
pgprintk("hfn old %lx new %lx\n",
- spte_to_pfn(*shadow_pte), pfn);
- rmap_remove(vcpu->kvm, shadow_pte);
+ spte_to_pfn(*sptep), pfn);
+ rmap_remove(vcpu->kvm, sptep);
} else
was_rmapped = 1;
}
- if (set_spte(vcpu, shadow_pte, pte_access, user_fault, write_fault,
- dirty, largepage, gfn, pfn, speculative, true)) {
+
+ if (set_spte(vcpu, sptep, pte_access, user_fault, write_fault,
+ dirty, level, gfn, pfn, speculative, true)) {
if (write_fault)
*ptwrite = 1;
kvm_x86_ops->tlb_flush(vcpu);
}
- pgprintk("%s: setting spte %llx\n", __func__, *shadow_pte);
+ pgprintk("%s: setting spte %llx\n", __func__, *sptep);
pgprintk("instantiating %s PTE (%s) at %ld (%llx) addr %p\n",
- is_large_pte(*shadow_pte)? "2MB" : "4kB",
- is_present_pte(*shadow_pte)?"RW":"R", gfn,
- *shadow_pte, shadow_pte);
- if (!was_rmapped && is_large_pte(*shadow_pte))
+ is_large_pte(*sptep)? "2MB" : "4kB",
+ *sptep & PT_PRESENT_MASK ?"RW":"R", gfn,
+ *sptep, sptep);
+ if (!was_rmapped && is_large_pte(*sptep))
++vcpu->kvm->stat.lpages;
- page_header_update_slot(vcpu->kvm, shadow_pte, gfn);
+ page_header_update_slot(vcpu->kvm, sptep, gfn);
if (!was_rmapped) {
- rmap_count = rmap_add(vcpu, shadow_pte, gfn, largepage);
- if (!is_rmap_pte(*shadow_pte))
+ rmap_count = rmap_add(vcpu, sptep, gfn);
+ if (!is_rmap_spte(*sptep))
kvm_release_pfn_clean(pfn);
if (rmap_count > RMAP_RECYCLE_THRESHOLD)
- rmap_recycle(vcpu, gfn, largepage);
+ rmap_recycle(vcpu, sptep, gfn);
} else {
if (was_writeble)
kvm_release_pfn_dirty(pfn);
@@ -1815,7 +1888,7 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
kvm_release_pfn_clean(pfn);
}
if (speculative) {
- vcpu->arch.last_pte_updated = shadow_pte;
+ vcpu->arch.last_pte_updated = sptep;
vcpu->arch.last_pte_gfn = gfn;
}
}
@@ -1825,7 +1898,7 @@ static void nonpaging_new_cr3(struct kvm_vcpu *vcpu)
}
static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,
- int largepage, gfn_t gfn, pfn_t pfn)
+ int level, gfn_t gfn, pfn_t pfn)
{
struct kvm_shadow_walk_iterator iterator;
struct kvm_mmu_page *sp;
@@ -1833,11 +1906,10 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,
gfn_t pseudo_gfn;
for_each_shadow_entry(vcpu, (u64)gfn << PAGE_SHIFT, iterator) {
- if (iterator.level == PT_PAGE_TABLE_LEVEL
- || (largepage && iterator.level == PT_DIRECTORY_LEVEL)) {
+ if (iterator.level == level) {
mmu_set_spte(vcpu, iterator.sptep, ACC_ALL, ACC_ALL,
0, write, 1, &pt_write,
- largepage, gfn, pfn, false);
+ level, gfn, pfn, false);
++vcpu->stat.pf_fixed;
break;
}
@@ -1853,10 +1925,10 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,
return -ENOMEM;
}
- set_shadow_pte(iterator.sptep,
- __pa(sp->spt)
- | PT_PRESENT_MASK | PT_WRITABLE_MASK
- | shadow_user_mask | shadow_x_mask);
+ __set_spte(iterator.sptep,
+ __pa(sp->spt)
+ | PT_PRESENT_MASK | PT_WRITABLE_MASK
+ | shadow_user_mask | shadow_x_mask);
}
}
return pt_write;
@@ -1865,14 +1937,20 @@ static int __direct_map(struct kvm_vcpu *vcpu, gpa_t v, int write,
static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)
{
int r;
- int largepage = 0;
+ int level;
pfn_t pfn;
unsigned long mmu_seq;
- if (is_largepage_backed(vcpu, gfn & ~(KVM_PAGES_PER_HPAGE-1))) {
- gfn &= ~(KVM_PAGES_PER_HPAGE-1);
- largepage = 1;
- }
+ level = mapping_level(vcpu, gfn);
+
+ /*
+ * This path builds a PAE pagetable - so we can map 2mb pages at
+ * maximum. Therefore check if the level is larger than that.
+ */
+ if (level > PT_DIRECTORY_LEVEL)
+ level = PT_DIRECTORY_LEVEL;
+
+ gfn &= ~(KVM_PAGES_PER_HPAGE(level) - 1);
mmu_seq = vcpu->kvm->mmu_notifier_seq;
smp_rmb();
@@ -1888,7 +1966,7 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)
if (mmu_notifier_retry(vcpu, mmu_seq))
goto out_unlock;
kvm_mmu_free_some_pages(vcpu);
- r = __direct_map(vcpu, v, write, largepage, gfn, pfn);
+ r = __direct_map(vcpu, v, write, level, gfn, pfn);
spin_unlock(&vcpu->kvm->mmu_lock);
@@ -1954,6 +2032,7 @@ static int mmu_alloc_roots(struct kvm_vcpu *vcpu)
gfn_t root_gfn;
struct kvm_mmu_page *sp;
int direct = 0;
+ u64 pdptr;
root_gfn = vcpu->arch.cr3 >> PAGE_SHIFT;
@@ -1981,11 +2060,12 @@ static int mmu_alloc_roots(struct kvm_vcpu *vcpu)
ASSERT(!VALID_PAGE(root));
if (vcpu->arch.mmu.root_level == PT32E_ROOT_LEVEL) {
- if (!is_present_pte(vcpu->arch.pdptrs[i])) {
+ pdptr = kvm_pdptr_read(vcpu, i);
+ if (!is_present_gpte(pdptr)) {
vcpu->arch.mmu.pae_root[i] = 0;
continue;
}
- root_gfn = vcpu->arch.pdptrs[i] >> PAGE_SHIFT;
+ root_gfn = pdptr >> PAGE_SHIFT;
} else if (vcpu->arch.mmu.root_level == 0)
root_gfn = 0;
if (mmu_check_root(vcpu, root_gfn))
@@ -2062,7 +2142,7 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa,
{
pfn_t pfn;
int r;
- int largepage = 0;
+ int level;
gfn_t gfn = gpa >> PAGE_SHIFT;
unsigned long mmu_seq;
@@ -2073,10 +2153,10 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa,
if (r)
return r;
- if (is_largepage_backed(vcpu, gfn & ~(KVM_PAGES_PER_HPAGE-1))) {
- gfn &= ~(KVM_PAGES_PER_HPAGE-1);
- largepage = 1;
- }
+ level = mapping_level(vcpu, gfn);
+
+ gfn &= ~(KVM_PAGES_PER_HPAGE(level) - 1);
+
mmu_seq = vcpu->kvm->mmu_notifier_seq;
smp_rmb();
pfn = gfn_to_pfn(vcpu->kvm, gfn);
@@ -2089,7 +2169,7 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa,
goto out_unlock;
kvm_mmu_free_some_pages(vcpu);
r = __direct_map(vcpu, gpa, error_code & PFERR_WRITE_MASK,
- largepage, gfn, pfn);
+ level, gfn, pfn);
spin_unlock(&vcpu->kvm->mmu_lock);
return r;
@@ -2206,7 +2286,9 @@ static void reset_rsvds_bits_mask(struct kvm_vcpu *vcpu, int level)
context->rsvd_bits_mask[0][0] = exb_bit_rsvd |
rsvd_bits(maxphyaddr, 51);
context->rsvd_bits_mask[1][3] = context->rsvd_bits_mask[0][3];
- context->rsvd_bits_mask[1][2] = context->rsvd_bits_mask[0][2];
+ context->rsvd_bits_mask[1][2] = exb_bit_rsvd |
+ rsvd_bits(maxphyaddr, 51) |
+ rsvd_bits(13, 29);
context->rsvd_bits_mask[1][1] = exb_bit_rsvd |
rsvd_bits(maxphyaddr, 51) |
rsvd_bits(13, 20); /* large page */
@@ -2357,8 +2439,8 @@ int kvm_mmu_load(struct kvm_vcpu *vcpu)
spin_unlock(&vcpu->kvm->mmu_lock);
if (r)
goto out;
+ /* set_cr3() should ensure TLB has been flushed */
kvm_x86_ops->set_cr3(vcpu, vcpu->arch.mmu.root_hpa);
- kvm_mmu_flush_tlb(vcpu);
out:
return r;
}
@@ -2378,15 +2460,14 @@ static void mmu_pte_write_zap_pte(struct kvm_vcpu *vcpu,
pte = *spte;
if (is_shadow_present_pte(pte)) {
- if (sp->role.level == PT_PAGE_TABLE_LEVEL ||
- is_large_pte(pte))
+ if (is_last_spte(pte, sp->role.level))
rmap_remove(vcpu->kvm, spte);
else {
child = page_header(pte & PT64_BASE_ADDR_MASK);
mmu_page_remove_parent_pte(child, spte);
}
}
- set_shadow_pte(spte, shadow_trap_nonpresent_pte);
+ __set_spte(spte, shadow_trap_nonpresent_pte);
if (is_large_pte(pte))
--vcpu->kvm->stat.lpages;
}
@@ -2397,11 +2478,8 @@ static void mmu_pte_write_new_pte(struct kvm_vcpu *vcpu,
const void *new)
{
if (sp->role.level != PT_PAGE_TABLE_LEVEL) {
- if (!vcpu->arch.update_pte.largepage ||
- sp->role.glevels == PT32_ROOT_LEVEL) {
- ++vcpu->kvm->stat.mmu_pde_zapped;
- return;
- }
+ ++vcpu->kvm->stat.mmu_pde_zapped;
+ return;
}
++vcpu->kvm->stat.mmu_pte_updated;
@@ -2447,8 +2525,6 @@ static void mmu_guess_page_from_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
u64 gpte = 0;
pfn_t pfn;
- vcpu->arch.update_pte.largepage = 0;
-
if (bytes != 4 && bytes != 8)
return;
@@ -2472,14 +2548,10 @@ static void mmu_guess_page_from_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
if ((bytes == 4) && (gpa % 4 == 0))
memcpy((void *)&gpte, new, 4);
}
- if (!is_present_pte(gpte))
+ if (!is_present_gpte(gpte))
return;
gfn = (gpte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT;
- if (is_large_pte(gpte) && is_largepage_backed(vcpu, gfn)) {
- gfn &= ~(KVM_PAGES_PER_HPAGE-1);
- vcpu->arch.update_pte.largepage = 1;
- }
vcpu->arch.update_pte.mmu_seq = vcpu->kvm->mmu_notifier_seq;
smp_rmb();
pfn = gfn_to_pfn(vcpu->kvm, gfn);
@@ -2622,6 +2694,9 @@ int kvm_mmu_unprotect_page_virt(struct kvm_vcpu *vcpu, gva_t gva)
gpa_t gpa;
int r;
+ if (tdp_enabled)
+ return 0;
+
gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, gva);
spin_lock(&vcpu->kvm->mmu_lock);
@@ -2633,7 +2708,8 @@ EXPORT_SYMBOL_GPL(kvm_mmu_unprotect_page_virt);
void __kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu)
{
- while (vcpu->kvm->arch.n_free_mmu_pages < KVM_REFILL_PAGES) {
+ while (vcpu->kvm->arch.n_free_mmu_pages < KVM_REFILL_PAGES &&
+ !list_empty(&vcpu->kvm->arch.active_mmu_pages)) {
struct kvm_mmu_page *sp;
sp = container_of(vcpu->kvm->arch.active_mmu_pages.prev,
@@ -2670,8 +2746,9 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u32 error_code)
++vcpu->stat.mmio_exits;
return 0;
case EMULATE_FAIL:
- kvm_report_emulation_failure(vcpu, "pagetable");
- return 1;
+ vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
+ vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
+ return 0;
default:
BUG();
}
@@ -2712,12 +2789,6 @@ static int alloc_mmu_pages(struct kvm_vcpu *vcpu)
ASSERT(vcpu);
- if (vcpu->kvm->arch.n_requested_mmu_pages)
- vcpu->kvm->arch.n_free_mmu_pages =
- vcpu->kvm->arch.n_requested_mmu_pages;
- else
- vcpu->kvm->arch.n_free_mmu_pages =
- vcpu->kvm->arch.n_alloc_mmu_pages;
/*
* When emulating 32-bit mode, cr3 is only 32 bits even on x86_64.
* Therefore we need to allocate shadow page tables in the first
@@ -3029,6 +3100,24 @@ out:
return r;
}
+int kvm_mmu_get_spte_hierarchy(struct kvm_vcpu *vcpu, u64 addr, u64 sptes[4])
+{
+ struct kvm_shadow_walk_iterator iterator;
+ int nr_sptes = 0;
+
+ spin_lock(&vcpu->kvm->mmu_lock);
+ for_each_shadow_entry(vcpu, addr, iterator) {
+ sptes[iterator.level-1] = *iterator.sptep;
+ nr_sptes++;
+ if (!is_shadow_present_pte(*iterator.sptep))
+ break;
+ }
+ spin_unlock(&vcpu->kvm->mmu_lock);
+
+ return nr_sptes;
+}
+EXPORT_SYMBOL_GPL(kvm_mmu_get_spte_hierarchy);
+
#ifdef AUDIT
static const char *audit_msg;
@@ -3041,6 +3130,54 @@ static gva_t canonicalize(gva_t gva)
return gva;
}
+
+typedef void (*inspect_spte_fn) (struct kvm *kvm, struct kvm_mmu_page *sp,
+ u64 *sptep);
+
+static void __mmu_spte_walk(struct kvm *kvm, struct kvm_mmu_page *sp,
+ inspect_spte_fn fn)
+{
+ int i;
+
+ for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
+ u64 ent = sp->spt[i];
+
+ if (is_shadow_present_pte(ent)) {
+ if (!is_last_spte(ent, sp->role.level)) {
+ struct kvm_mmu_page *child;
+ child = page_header(ent & PT64_BASE_ADDR_MASK);
+ __mmu_spte_walk(kvm, child, fn);
+ } else
+ fn(kvm, sp, &sp->spt[i]);
+ }
+ }
+}
+
+static void mmu_spte_walk(struct kvm_vcpu *vcpu, inspect_spte_fn fn)
+{
+ int i;
+ struct kvm_mmu_page *sp;
+
+ if (!VALID_PAGE(vcpu->arch.mmu.root_hpa))
+ return;
+ if (vcpu->arch.mmu.shadow_root_level == PT64_ROOT_LEVEL) {
+ hpa_t root = vcpu->arch.mmu.root_hpa;
+ sp = page_header(root);
+ __mmu_spte_walk(vcpu->kvm, sp, fn);
+ return;
+ }
+ for (i = 0; i < 4; ++i) {
+ hpa_t root = vcpu->arch.mmu.pae_root[i];
+
+ if (root && VALID_PAGE(root)) {
+ root &= PT64_BASE_ADDR_MASK;
+ sp = page_header(root);
+ __mmu_spte_walk(vcpu->kvm, sp, fn);
+ }
+ }
+ return;
+}
+
static void audit_mappings_page(struct kvm_vcpu *vcpu, u64 page_pte,
gva_t va, int level)
{
@@ -3055,20 +3192,19 @@ static void audit_mappings_page(struct kvm_vcpu *vcpu, u64 page_pte,
continue;
va = canonicalize(va);
- if (level > 1) {
- if (ent == shadow_notrap_nonpresent_pte)
- printk(KERN_ERR "audit: (%s) nontrapping pte"
- " in nonleaf level: levels %d gva %lx"
- " level %d pte %llx\n", audit_msg,
- vcpu->arch.mmu.root_level, va, level, ent);
- else
- audit_mappings_page(vcpu, ent, va, level - 1);
- } else {
+ if (is_shadow_present_pte(ent) && !is_last_spte(ent, level))
+ audit_mappings_page(vcpu, ent, va, level - 1);
+ else {
gpa_t gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, va);
gfn_t gfn = gpa >> PAGE_SHIFT;
pfn_t pfn = gfn_to_pfn(vcpu->kvm, gfn);
hpa_t hpa = (hpa_t)pfn << PAGE_SHIFT;
+ if (is_error_pfn(pfn)) {
+ kvm_release_pfn_clean(pfn);
+ continue;
+ }
+
if (is_shadow_present_pte(ent)
&& (ent & PT64_BASE_ADDR_MASK) != hpa)
printk(KERN_ERR "xx audit error: (%s) levels %d"
@@ -3122,7 +3258,7 @@ static int count_rmaps(struct kvm_vcpu *vcpu)
d = (struct kvm_rmap_desc *)(*rmapp & ~1ul);
while (d) {
for (k = 0; k < RMAP_EXT; ++k)
- if (d->shadow_ptes[k])
+ if (d->sptes[k])
++nmaps;
else
break;
@@ -3133,9 +3269,48 @@ static int count_rmaps(struct kvm_vcpu *vcpu)
return nmaps;
}
-static int count_writable_mappings(struct kvm_vcpu *vcpu)
+void inspect_spte_has_rmap(struct kvm *kvm, struct kvm_mmu_page *sp, u64 *sptep)
+{
+ unsigned long *rmapp;
+ struct kvm_mmu_page *rev_sp;
+ gfn_t gfn;
+
+ if (*sptep & PT_WRITABLE_MASK) {
+ rev_sp = page_header(__pa(sptep));
+ gfn = rev_sp->gfns[sptep - rev_sp->spt];
+
+ if (!gfn_to_memslot(kvm, gfn)) {
+ if (!printk_ratelimit())
+ return;
+ printk(KERN_ERR "%s: no memslot for gfn %ld\n",
+ audit_msg, gfn);
+ printk(KERN_ERR "%s: index %ld of sp (gfn=%lx)\n",
+ audit_msg, sptep - rev_sp->spt,
+ rev_sp->gfn);
+ dump_stack();
+ return;
+ }
+
+ rmapp = gfn_to_rmap(kvm, rev_sp->gfns[sptep - rev_sp->spt],
+ is_large_pte(*sptep));
+ if (!*rmapp) {
+ if (!printk_ratelimit())
+ return;
+ printk(KERN_ERR "%s: no rmap for writable spte %llx\n",
+ audit_msg, *sptep);
+ dump_stack();
+ }
+ }
+
+}
+
+void audit_writable_sptes_have_rmaps(struct kvm_vcpu *vcpu)
+{
+ mmu_spte_walk(vcpu, inspect_spte_has_rmap);
+}
+
+static void check_writable_mappings_rmap(struct kvm_vcpu *vcpu)
{
- int nmaps = 0;
struct kvm_mmu_page *sp;
int i;
@@ -3152,20 +3327,16 @@ static int count_writable_mappings(struct kvm_vcpu *vcpu)
continue;
if (!(ent & PT_WRITABLE_MASK))
continue;
- ++nmaps;
+ inspect_spte_has_rmap(vcpu->kvm, sp, &pt[i]);
}
}
- return nmaps;
+ return;
}
static void audit_rmap(struct kvm_vcpu *vcpu)
{
- int n_rmap = count_rmaps(vcpu);
- int n_actual = count_writable_mappings(vcpu);
-
- if (n_rmap != n_actual)
- printk(KERN_ERR "%s: (%s) rmap %d actual %d\n",
- __func__, audit_msg, n_rmap, n_actual);
+ check_writable_mappings_rmap(vcpu);
+ count_rmaps(vcpu);
}
static void audit_write_protection(struct kvm_vcpu *vcpu)
@@ -3173,20 +3344,28 @@ static void audit_write_protection(struct kvm_vcpu *vcpu)
struct kvm_mmu_page *sp;
struct kvm_memory_slot *slot;
unsigned long *rmapp;
+ u64 *spte;
gfn_t gfn;
list_for_each_entry(sp, &vcpu->kvm->arch.active_mmu_pages, link) {
if (sp->role.direct)
continue;
+ if (sp->unsync)
+ continue;
gfn = unalias_gfn(vcpu->kvm, sp->gfn);
slot = gfn_to_memslot_unaliased(vcpu->kvm, sp->gfn);
rmapp = &slot->rmap[gfn - slot->base_gfn];
- if (*rmapp)
- printk(KERN_ERR "%s: (%s) shadow page has writable"
- " mappings: gfn %lx role %x\n",
+
+ spte = rmap_next(vcpu->kvm, rmapp, NULL);
+ while (spte) {
+ if (*spte & PT_WRITABLE_MASK)
+ printk(KERN_ERR "%s: (%s) shadow page has "
+ "writable mappings: gfn %lx role %x\n",
__func__, audit_msg, sp->gfn,
sp->role.word);
+ spte = rmap_next(vcpu->kvm, rmapp, spte);
+ }
}
}
@@ -3198,7 +3377,9 @@ static void kvm_mmu_audit(struct kvm_vcpu *vcpu, const char *msg)
audit_msg = msg;
audit_rmap(vcpu);
audit_write_protection(vcpu);
- audit_mappings(vcpu);
+ if (strcmp("pre pte write", audit_msg) != 0)
+ audit_mappings(vcpu);
+ audit_writable_sptes_have_rmaps(vcpu);
dbg = olddbg;
}
diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h
index 3494a2fb136..61a1b3884b4 100644
--- a/arch/x86/kvm/mmu.h
+++ b/arch/x86/kvm/mmu.h
@@ -37,6 +37,8 @@
#define PT32_ROOT_LEVEL 2
#define PT32E_ROOT_LEVEL 3
+int kvm_mmu_get_spte_hierarchy(struct kvm_vcpu *vcpu, u64 addr, u64 sptes[4]);
+
static inline void kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu)
{
if (unlikely(vcpu->kvm->arch.n_free_mmu_pages < KVM_MIN_FREE_MMU_PAGES))
@@ -75,7 +77,7 @@ static inline int is_paging(struct kvm_vcpu *vcpu)
return vcpu->arch.cr0 & X86_CR0_PG;
}
-static inline int is_present_pte(unsigned long pte)
+static inline int is_present_gpte(unsigned long pte)
{
return pte & PT_PRESENT_MASK;
}
diff --git a/arch/x86/kvm/mmutrace.h b/arch/x86/kvm/mmutrace.h
new file mode 100644
index 00000000000..3e4a5c6ca2a
--- /dev/null
+++ b/arch/x86/kvm/mmutrace.h
@@ -0,0 +1,220 @@
+#if !defined(_TRACE_KVMMMU_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_KVMMMU_H
+
+#include <linux/tracepoint.h>
+#include <linux/ftrace_event.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM kvmmmu
+#define TRACE_INCLUDE_PATH .
+#define TRACE_INCLUDE_FILE mmutrace
+
+#define KVM_MMU_PAGE_FIELDS \
+ __field(__u64, gfn) \
+ __field(__u32, role) \
+ __field(__u32, root_count) \
+ __field(__u32, unsync)
+
+#define KVM_MMU_PAGE_ASSIGN(sp) \
+ __entry->gfn = sp->gfn; \
+ __entry->role = sp->role.word; \
+ __entry->root_count = sp->root_count; \
+ __entry->unsync = sp->unsync;
+
+#define KVM_MMU_PAGE_PRINTK() ({ \
+ const char *ret = p->buffer + p->len; \
+ static const char *access_str[] = { \
+ "---", "--x", "w--", "w-x", "-u-", "-ux", "wu-", "wux" \
+ }; \
+ union kvm_mmu_page_role role; \
+ \
+ role.word = __entry->role; \
+ \
+ trace_seq_printf(p, "sp gfn %llx %u/%u q%u%s %s%s %spge" \
+ " %snxe root %u %s%c", \
+ __entry->gfn, role.level, role.glevels, \
+ role.quadrant, \
+ role.direct ? " direct" : "", \
+ access_str[role.access], \
+ role.invalid ? " invalid" : "", \
+ role.cr4_pge ? "" : "!", \
+ role.nxe ? "" : "!", \
+ __entry->root_count, \
+ __entry->unsync ? "unsync" : "sync", 0); \
+ ret; \
+ })
+
+#define kvm_mmu_trace_pferr_flags \
+ { PFERR_PRESENT_MASK, "P" }, \
+ { PFERR_WRITE_MASK, "W" }, \
+ { PFERR_USER_MASK, "U" }, \
+ { PFERR_RSVD_MASK, "RSVD" }, \
+ { PFERR_FETCH_MASK, "F" }
+
+/*
+ * A pagetable walk has started
+ */
+TRACE_EVENT(
+ kvm_mmu_pagetable_walk,
+ TP_PROTO(u64 addr, int write_fault, int user_fault, int fetch_fault),
+ TP_ARGS(addr, write_fault, user_fault, fetch_fault),
+
+ TP_STRUCT__entry(
+ __field(__u64, addr)
+ __field(__u32, pferr)
+ ),
+
+ TP_fast_assign(
+ __entry->addr = addr;
+ __entry->pferr = (!!write_fault << 1) | (!!user_fault << 2)
+ | (!!fetch_fault << 4);
+ ),
+
+ TP_printk("addr %llx pferr %x %s", __entry->addr, __entry->pferr,
+ __print_flags(__entry->pferr, "|", kvm_mmu_trace_pferr_flags))
+);
+
+
+/* We just walked a paging element */
+TRACE_EVENT(
+ kvm_mmu_paging_element,
+ TP_PROTO(u64 pte, int level),
+ TP_ARGS(pte, level),
+
+ TP_STRUCT__entry(
+ __field(__u64, pte)
+ __field(__u32, level)
+ ),
+
+ TP_fast_assign(
+ __entry->pte = pte;
+ __entry->level = level;
+ ),
+
+ TP_printk("pte %llx level %u", __entry->pte, __entry->level)
+);
+
+/* We set a pte accessed bit */
+TRACE_EVENT(
+ kvm_mmu_set_accessed_bit,
+ TP_PROTO(unsigned long table_gfn, unsigned index, unsigned size),
+ TP_ARGS(table_gfn, index, size),
+
+ TP_STRUCT__entry(
+ __field(__u64, gpa)
+ ),
+
+ TP_fast_assign(
+ __entry->gpa = ((u64)table_gfn << PAGE_SHIFT)
+ + index * size;
+ ),
+
+ TP_printk("gpa %llx", __entry->gpa)
+);
+
+/* We set a pte dirty bit */
+TRACE_EVENT(
+ kvm_mmu_set_dirty_bit,
+ TP_PROTO(unsigned long table_gfn, unsigned index, unsigned size),
+ TP_ARGS(table_gfn, index, size),
+
+ TP_STRUCT__entry(
+ __field(__u64, gpa)
+ ),
+
+ TP_fast_assign(
+ __entry->gpa = ((u64)table_gfn << PAGE_SHIFT)
+ + index * size;
+ ),
+
+ TP_printk("gpa %llx", __entry->gpa)
+);
+
+TRACE_EVENT(
+ kvm_mmu_walker_error,
+ TP_PROTO(u32 pferr),
+ TP_ARGS(pferr),
+
+ TP_STRUCT__entry(
+ __field(__u32, pferr)
+ ),
+
+ TP_fast_assign(
+ __entry->pferr = pferr;
+ ),
+
+ TP_printk("pferr %x %s", __entry->pferr,
+ __print_flags(__entry->pferr, "|", kvm_mmu_trace_pferr_flags))
+);
+
+TRACE_EVENT(
+ kvm_mmu_get_page,
+ TP_PROTO(struct kvm_mmu_page *sp, bool created),
+ TP_ARGS(sp, created),
+
+ TP_STRUCT__entry(
+ KVM_MMU_PAGE_FIELDS
+ __field(bool, created)
+ ),
+
+ TP_fast_assign(
+ KVM_MMU_PAGE_ASSIGN(sp)
+ __entry->created = created;
+ ),
+
+ TP_printk("%s %s", KVM_MMU_PAGE_PRINTK(),
+ __entry->created ? "new" : "existing")
+);
+
+TRACE_EVENT(
+ kvm_mmu_sync_page,
+ TP_PROTO(struct kvm_mmu_page *sp),
+ TP_ARGS(sp),
+
+ TP_STRUCT__entry(
+ KVM_MMU_PAGE_FIELDS
+ ),
+
+ TP_fast_assign(
+ KVM_MMU_PAGE_ASSIGN(sp)
+ ),
+
+ TP_printk("%s", KVM_MMU_PAGE_PRINTK())
+);
+
+TRACE_EVENT(
+ kvm_mmu_unsync_page,
+ TP_PROTO(struct kvm_mmu_page *sp),
+ TP_ARGS(sp),
+
+ TP_STRUCT__entry(
+ KVM_MMU_PAGE_FIELDS
+ ),
+
+ TP_fast_assign(
+ KVM_MMU_PAGE_ASSIGN(sp)
+ ),
+
+ TP_printk("%s", KVM_MMU_PAGE_PRINTK())
+);
+
+TRACE_EVENT(
+ kvm_mmu_zap_page,
+ TP_PROTO(struct kvm_mmu_page *sp),
+ TP_ARGS(sp),
+
+ TP_STRUCT__entry(
+ KVM_MMU_PAGE_FIELDS
+ ),
+
+ TP_fast_assign(
+ KVM_MMU_PAGE_ASSIGN(sp)
+ ),
+
+ TP_printk("%s", KVM_MMU_PAGE_PRINTK())
+);
+
+#endif /* _TRACE_KVMMMU_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
index 67785f63539..d2fec9c12d2 100644
--- a/arch/x86/kvm/paging_tmpl.h
+++ b/arch/x86/kvm/paging_tmpl.h
@@ -27,7 +27,8 @@
#define guest_walker guest_walker64
#define FNAME(name) paging##64_##name
#define PT_BASE_ADDR_MASK PT64_BASE_ADDR_MASK
- #define PT_DIR_BASE_ADDR_MASK PT64_DIR_BASE_ADDR_MASK
+ #define PT_LVL_ADDR_MASK(lvl) PT64_LVL_ADDR_MASK(lvl)
+ #define PT_LVL_OFFSET_MASK(lvl) PT64_LVL_OFFSET_MASK(lvl)
#define PT_INDEX(addr, level) PT64_INDEX(addr, level)
#define PT_LEVEL_MASK(level) PT64_LEVEL_MASK(level)
#define PT_LEVEL_BITS PT64_LEVEL_BITS
@@ -43,7 +44,8 @@
#define guest_walker guest_walker32
#define FNAME(name) paging##32_##name
#define PT_BASE_ADDR_MASK PT32_BASE_ADDR_MASK
- #define PT_DIR_BASE_ADDR_MASK PT32_DIR_BASE_ADDR_MASK
+ #define PT_LVL_ADDR_MASK(lvl) PT32_LVL_ADDR_MASK(lvl)
+ #define PT_LVL_OFFSET_MASK(lvl) PT32_LVL_OFFSET_MASK(lvl)
#define PT_INDEX(addr, level) PT32_INDEX(addr, level)
#define PT_LEVEL_MASK(level) PT32_LEVEL_MASK(level)
#define PT_LEVEL_BITS PT32_LEVEL_BITS
@@ -53,8 +55,8 @@
#error Invalid PTTYPE value
#endif
-#define gpte_to_gfn FNAME(gpte_to_gfn)
-#define gpte_to_gfn_pde FNAME(gpte_to_gfn_pde)
+#define gpte_to_gfn_lvl FNAME(gpte_to_gfn_lvl)
+#define gpte_to_gfn(pte) gpte_to_gfn_lvl((pte), PT_PAGE_TABLE_LEVEL)
/*
* The guest_walker structure emulates the behavior of the hardware page
@@ -71,14 +73,9 @@ struct guest_walker {
u32 error_code;
};
-static gfn_t gpte_to_gfn(pt_element_t gpte)
+static gfn_t gpte_to_gfn_lvl(pt_element_t gpte, int lvl)
{
- return (gpte & PT_BASE_ADDR_MASK) >> PAGE_SHIFT;
-}
-
-static gfn_t gpte_to_gfn_pde(pt_element_t gpte)
-{
- return (gpte & PT_DIR_BASE_ADDR_MASK) >> PAGE_SHIFT;
+ return (gpte & PT_LVL_ADDR_MASK(lvl)) >> PAGE_SHIFT;
}
static bool FNAME(cmpxchg_gpte)(struct kvm *kvm,
@@ -125,14 +122,16 @@ static int FNAME(walk_addr)(struct guest_walker *walker,
gpa_t pte_gpa;
int rsvd_fault = 0;
- pgprintk("%s: addr %lx\n", __func__, addr);
+ trace_kvm_mmu_pagetable_walk(addr, write_fault, user_fault,
+ fetch_fault);
walk:
walker->level = vcpu->arch.mmu.root_level;
pte = vcpu->arch.cr3;
#if PTTYPE == 64
if (!is_long_mode(vcpu)) {
- pte = vcpu->arch.pdptrs[(addr >> 30) & 3];
- if (!is_present_pte(pte))
+ pte = kvm_pdptr_read(vcpu, (addr >> 30) & 3);
+ trace_kvm_mmu_paging_element(pte, walker->level);
+ if (!is_present_gpte(pte))
goto not_present;
--walker->level;
}
@@ -150,12 +149,11 @@ walk:
pte_gpa += index * sizeof(pt_element_t);
walker->table_gfn[walker->level - 1] = table_gfn;
walker->pte_gpa[walker->level - 1] = pte_gpa;
- pgprintk("%s: table_gfn[%d] %lx\n", __func__,
- walker->level - 1, table_gfn);
kvm_read_guest(vcpu->kvm, pte_gpa, &pte, sizeof(pte));
+ trace_kvm_mmu_paging_element(pte, walker->level);
- if (!is_present_pte(pte))
+ if (!is_present_gpte(pte))
goto not_present;
rsvd_fault = is_rsvd_bits_set(vcpu, pte, walker->level);
@@ -175,6 +173,8 @@ walk:
#endif
if (!(pte & PT_ACCESSED_MASK)) {
+ trace_kvm_mmu_set_accessed_bit(table_gfn, index,
+ sizeof(pte));
mark_page_dirty(vcpu->kvm, table_gfn);
if (FNAME(cmpxchg_gpte)(vcpu->kvm, table_gfn,
index, pte, pte|PT_ACCESSED_MASK))
@@ -186,18 +186,24 @@ walk:
walker->ptes[walker->level - 1] = pte;
- if (walker->level == PT_PAGE_TABLE_LEVEL) {
- walker->gfn = gpte_to_gfn(pte);
- break;
- }
-
- if (walker->level == PT_DIRECTORY_LEVEL
- && (pte & PT_PAGE_SIZE_MASK)
- && (PTTYPE == 64 || is_pse(vcpu))) {
- walker->gfn = gpte_to_gfn_pde(pte);
- walker->gfn += PT_INDEX(addr, PT_PAGE_TABLE_LEVEL);
- if (PTTYPE == 32 && is_cpuid_PSE36())
+ if ((walker->level == PT_PAGE_TABLE_LEVEL) ||
+ ((walker->level == PT_DIRECTORY_LEVEL) &&
+ (pte & PT_PAGE_SIZE_MASK) &&
+ (PTTYPE == 64 || is_pse(vcpu))) ||
+ ((walker->level == PT_PDPE_LEVEL) &&
+ (pte & PT_PAGE_SIZE_MASK) &&
+ is_long_mode(vcpu))) {
+ int lvl = walker->level;
+
+ walker->gfn = gpte_to_gfn_lvl(pte, lvl);
+ walker->gfn += (addr & PT_LVL_OFFSET_MASK(lvl))
+ >> PAGE_SHIFT;
+
+ if (PTTYPE == 32 &&
+ walker->level == PT_DIRECTORY_LEVEL &&
+ is_cpuid_PSE36())
walker->gfn += pse36_gfn_delta(pte);
+
break;
}
@@ -205,9 +211,10 @@ walk:
--walker->level;
}
- if (write_fault && !is_dirty_pte(pte)) {
+ if (write_fault && !is_dirty_gpte(pte)) {
bool ret;
+ trace_kvm_mmu_set_dirty_bit(table_gfn, index, sizeof(pte));
mark_page_dirty(vcpu->kvm, table_gfn);
ret = FNAME(cmpxchg_gpte)(vcpu->kvm, table_gfn, index, pte,
pte|PT_DIRTY_MASK);
@@ -239,6 +246,7 @@ err:
walker->error_code |= PFERR_FETCH_MASK;
if (rsvd_fault)
walker->error_code |= PFERR_RSVD_MASK;
+ trace_kvm_mmu_walker_error(walker->error_code);
return 0;
}
@@ -248,12 +256,11 @@ static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *page,
pt_element_t gpte;
unsigned pte_access;
pfn_t pfn;
- int largepage = vcpu->arch.update_pte.largepage;
gpte = *(const pt_element_t *)pte;
if (~gpte & (PT_PRESENT_MASK | PT_ACCESSED_MASK)) {
- if (!is_present_pte(gpte))
- set_shadow_pte(spte, shadow_notrap_nonpresent_pte);
+ if (!is_present_gpte(gpte))
+ __set_spte(spte, shadow_notrap_nonpresent_pte);
return;
}
pgprintk("%s: gpte %llx spte %p\n", __func__, (u64)gpte, spte);
@@ -267,7 +274,7 @@ static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *page,
return;
kvm_get_pfn(pfn);
mmu_set_spte(vcpu, spte, page->role.access, pte_access, 0, 0,
- gpte & PT_DIRTY_MASK, NULL, largepage,
+ gpte & PT_DIRTY_MASK, NULL, PT_PAGE_TABLE_LEVEL,
gpte_to_gfn(gpte), pfn, true);
}
@@ -276,7 +283,7 @@ static void FNAME(update_pte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *page,
*/
static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
struct guest_walker *gw,
- int user_fault, int write_fault, int largepage,
+ int user_fault, int write_fault, int hlevel,
int *ptwrite, pfn_t pfn)
{
unsigned access = gw->pt_access;
@@ -289,19 +296,18 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
pt_element_t curr_pte;
struct kvm_shadow_walk_iterator iterator;
- if (!is_present_pte(gw->ptes[gw->level - 1]))
+ if (!is_present_gpte(gw->ptes[gw->level - 1]))
return NULL;
for_each_shadow_entry(vcpu, addr, iterator) {
level = iterator.level;
sptep = iterator.sptep;
- if (level == PT_PAGE_TABLE_LEVEL
- || (largepage && level == PT_DIRECTORY_LEVEL)) {
+ if (iterator.level == hlevel) {
mmu_set_spte(vcpu, sptep, access,
gw->pte_access & access,
user_fault, write_fault,
gw->ptes[gw->level-1] & PT_DIRTY_MASK,
- ptwrite, largepage,
+ ptwrite, level,
gw->gfn, pfn, false);
break;
}
@@ -311,16 +317,19 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
if (is_large_pte(*sptep)) {
rmap_remove(vcpu->kvm, sptep);
- set_shadow_pte(sptep, shadow_trap_nonpresent_pte);
+ __set_spte(sptep, shadow_trap_nonpresent_pte);
kvm_flush_remote_tlbs(vcpu->kvm);
}
- if (level == PT_DIRECTORY_LEVEL
- && gw->level == PT_DIRECTORY_LEVEL) {
+ if (level <= gw->level) {
+ int delta = level - gw->level + 1;
direct = 1;
- if (!is_dirty_pte(gw->ptes[level - 1]))
+ if (!is_dirty_gpte(gw->ptes[level - delta]))
access &= ~ACC_WRITE_MASK;
- table_gfn = gpte_to_gfn(gw->ptes[level - 1]);
+ table_gfn = gpte_to_gfn(gw->ptes[level - delta]);
+ /* advance table_gfn when emulating 1gb pages with 4k */
+ if (delta == 0)
+ table_gfn += PT_INDEX(addr, level);
} else {
direct = 0;
table_gfn = gw->table_gfn[level - 2];
@@ -369,11 +378,11 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
int user_fault = error_code & PFERR_USER_MASK;
int fetch_fault = error_code & PFERR_FETCH_MASK;
struct guest_walker walker;
- u64 *shadow_pte;
+ u64 *sptep;
int write_pt = 0;
int r;
pfn_t pfn;
- int largepage = 0;
+ int level = PT_PAGE_TABLE_LEVEL;
unsigned long mmu_seq;
pgprintk("%s: addr %lx err %x\n", __func__, addr, error_code);
@@ -399,14 +408,11 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
return 0;
}
- if (walker.level == PT_DIRECTORY_LEVEL) {
- gfn_t large_gfn;
- large_gfn = walker.gfn & ~(KVM_PAGES_PER_HPAGE-1);
- if (is_largepage_backed(vcpu, large_gfn)) {
- walker.gfn = large_gfn;
- largepage = 1;
- }
+ if (walker.level >= PT_DIRECTORY_LEVEL) {
+ level = min(walker.level, mapping_level(vcpu, walker.gfn));
+ walker.gfn = walker.gfn & ~(KVM_PAGES_PER_HPAGE(level) - 1);
}
+
mmu_seq = vcpu->kvm->mmu_notifier_seq;
smp_rmb();
pfn = gfn_to_pfn(vcpu->kvm, walker.gfn);
@@ -422,11 +428,10 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
if (mmu_notifier_retry(vcpu, mmu_seq))
goto out_unlock;
kvm_mmu_free_some_pages(vcpu);
- shadow_pte = FNAME(fetch)(vcpu, addr, &walker, user_fault, write_fault,
- largepage, &write_pt, pfn);
-
+ sptep = FNAME(fetch)(vcpu, addr, &walker, user_fault, write_fault,
+ level, &write_pt, pfn);
pgprintk("%s: shadow pte %p %llx ptwrite %d\n", __func__,
- shadow_pte, *shadow_pte, write_pt);
+ sptep, *sptep, write_pt);
if (!write_pt)
vcpu->arch.last_pt_write_count = 0; /* reset fork detector */
@@ -459,8 +464,9 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva)
sptep = iterator.sptep;
/* FIXME: properly handle invlpg on large guest pages */
- if (level == PT_PAGE_TABLE_LEVEL ||
- ((level == PT_DIRECTORY_LEVEL) && is_large_pte(*sptep))) {
+ if (level == PT_PAGE_TABLE_LEVEL ||
+ ((level == PT_DIRECTORY_LEVEL && is_large_pte(*sptep))) ||
+ ((level == PT_PDPE_LEVEL && is_large_pte(*sptep)))) {
struct kvm_mmu_page *sp = page_header(__pa(sptep));
pte_gpa = (sp->gfn << PAGE_SHIFT);
@@ -472,7 +478,7 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva)
--vcpu->kvm->stat.lpages;
need_flush = 1;
}
- set_shadow_pte(sptep, shadow_trap_nonpresent_pte);
+ __set_spte(sptep, shadow_trap_nonpresent_pte);
break;
}
@@ -489,7 +495,7 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva)
if (kvm_read_guest_atomic(vcpu->kvm, pte_gpa, &gpte,
sizeof(pt_element_t)))
return;
- if (is_present_pte(gpte) && (gpte & PT_ACCESSED_MASK)) {
+ if (is_present_gpte(gpte) && (gpte & PT_ACCESSED_MASK)) {
if (mmu_topup_memory_caches(vcpu))
return;
kvm_mmu_pte_write(vcpu, pte_gpa, (const u8 *)&gpte,
@@ -536,7 +542,7 @@ static void FNAME(prefetch_page)(struct kvm_vcpu *vcpu,
r = kvm_read_guest_atomic(vcpu->kvm, pte_gpa, pt, sizeof pt);
pte_gpa += ARRAY_SIZE(pt) * sizeof(pt_element_t);
for (j = 0; j < ARRAY_SIZE(pt); ++j)
- if (r || is_present_pte(pt[j]))
+ if (r || is_present_gpte(pt[j]))
sp->spt[i+j] = shadow_trap_nonpresent_pte;
else
sp->spt[i+j] = shadow_notrap_nonpresent_pte;
@@ -574,23 +580,23 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
sizeof(pt_element_t)))
return -EINVAL;
- if (gpte_to_gfn(gpte) != gfn || !is_present_pte(gpte) ||
+ if (gpte_to_gfn(gpte) != gfn || !is_present_gpte(gpte) ||
!(gpte & PT_ACCESSED_MASK)) {
u64 nonpresent;
rmap_remove(vcpu->kvm, &sp->spt[i]);
- if (is_present_pte(gpte))
+ if (is_present_gpte(gpte))
nonpresent = shadow_trap_nonpresent_pte;
else
nonpresent = shadow_notrap_nonpresent_pte;
- set_shadow_pte(&sp->spt[i], nonpresent);
+ __set_spte(&sp->spt[i], nonpresent);
continue;
}
nr_present++;
pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
set_spte(vcpu, &sp->spt[i], pte_access, 0, 0,
- is_dirty_pte(gpte), 0, gfn,
+ is_dirty_gpte(gpte), PT_PAGE_TABLE_LEVEL, gfn,
spte_to_pfn(sp->spt[i]), true, false);
}
@@ -603,9 +609,10 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
#undef PT_BASE_ADDR_MASK
#undef PT_INDEX
#undef PT_LEVEL_MASK
-#undef PT_DIR_BASE_ADDR_MASK
+#undef PT_LVL_ADDR_MASK
+#undef PT_LVL_OFFSET_MASK
#undef PT_LEVEL_BITS
#undef PT_MAX_FULL_LEVELS
#undef gpte_to_gfn
-#undef gpte_to_gfn_pde
+#undef gpte_to_gfn_lvl
#undef CMPXCHG
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index b1f658ad2f0..944cc9c04b3 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -15,7 +15,6 @@
*/
#include <linux/kvm_host.h>
-#include "kvm_svm.h"
#include "irq.h"
#include "mmu.h"
#include "kvm_cache_regs.h"
@@ -26,10 +25,12 @@
#include <linux/vmalloc.h>
#include <linux/highmem.h>
#include <linux/sched.h>
+#include <linux/ftrace_event.h>
#include <asm/desc.h>
#include <asm/virtext.h>
+#include "trace.h"
#define __ex(x) __kvm_handle_fault_on_reboot(x)
@@ -46,6 +47,10 @@ MODULE_LICENSE("GPL");
#define SVM_FEATURE_LBRV (1 << 1)
#define SVM_FEATURE_SVML (1 << 2)
+#define NESTED_EXIT_HOST 0 /* Exit handled on host level */
+#define NESTED_EXIT_DONE 1 /* Exit caused nested vmexit */
+#define NESTED_EXIT_CONTINUE 2 /* Further checks needed */
+
#define DEBUGCTL_RESERVED_BITS (~(0x3fULL))
/* Turn on to get debugging output*/
@@ -57,6 +62,58 @@ MODULE_LICENSE("GPL");
#define nsvm_printk(fmt, args...) do {} while(0)
#endif
+static const u32 host_save_user_msrs[] = {
+#ifdef CONFIG_X86_64
+ MSR_STAR, MSR_LSTAR, MSR_CSTAR, MSR_SYSCALL_MASK, MSR_KERNEL_GS_BASE,
+ MSR_FS_BASE,
+#endif
+ MSR_IA32_SYSENTER_CS, MSR_IA32_SYSENTER_ESP, MSR_IA32_SYSENTER_EIP,
+};
+
+#define NR_HOST_SAVE_USER_MSRS ARRAY_SIZE(host_save_user_msrs)
+
+struct kvm_vcpu;
+
+struct nested_state {
+ struct vmcb *hsave;
+ u64 hsave_msr;
+ u64 vmcb;
+
+ /* These are the merged vectors */
+ u32 *msrpm;
+
+ /* gpa pointers to the real vectors */
+ u64 vmcb_msrpm;
+
+ /* cache for intercepts of the guest */
+ u16 intercept_cr_read;
+ u16 intercept_cr_write;
+ u16 intercept_dr_read;
+ u16 intercept_dr_write;
+ u32 intercept_exceptions;
+ u64 intercept;
+
+};
+
+struct vcpu_svm {
+ struct kvm_vcpu vcpu;
+ struct vmcb *vmcb;
+ unsigned long vmcb_pa;
+ struct svm_cpu_data *svm_data;
+ uint64_t asid_generation;
+ uint64_t sysenter_esp;
+ uint64_t sysenter_eip;
+
+ u64 next_rip;
+
+ u64 host_user_msrs[NR_HOST_SAVE_USER_MSRS];
+ u64 host_gs_base;
+
+ u32 *msrpm;
+
+ struct nested_state nested;
+};
+
/* enable NPT for AMD64 and X86 with PAE */
#if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE)
static bool npt_enabled = true;
@@ -67,15 +124,14 @@ static int npt = 1;
module_param(npt, int, S_IRUGO);
-static int nested = 0;
+static int nested = 1;
module_param(nested, int, S_IRUGO);
static void svm_flush_tlb(struct kvm_vcpu *vcpu);
+static void svm_complete_interrupts(struct vcpu_svm *svm);
-static int nested_svm_exit_handled(struct vcpu_svm *svm, bool kvm_override);
+static int nested_svm_exit_handled(struct vcpu_svm *svm);
static int nested_svm_vmexit(struct vcpu_svm *svm);
-static int nested_svm_vmsave(struct vcpu_svm *svm, void *nested_vmcb,
- void *arg2, void *opaque);
static int nested_svm_check_exception(struct vcpu_svm *svm, unsigned nr,
bool has_error_code, u32 error_code);
@@ -86,7 +142,22 @@ static inline struct vcpu_svm *to_svm(struct kvm_vcpu *vcpu)
static inline bool is_nested(struct vcpu_svm *svm)
{
- return svm->nested_vmcb;
+ return svm->nested.vmcb;
+}
+
+static inline void enable_gif(struct vcpu_svm *svm)
+{
+ svm->vcpu.arch.hflags |= HF_GIF_MASK;
+}
+
+static inline void disable_gif(struct vcpu_svm *svm)
+{
+ svm->vcpu.arch.hflags &= ~HF_GIF_MASK;
+}
+
+static inline bool gif_set(struct vcpu_svm *svm)
+{
+ return !!(svm->vcpu.arch.hflags & HF_GIF_MASK);
}
static unsigned long iopm_base;
@@ -147,19 +218,6 @@ static inline void invlpga(unsigned long addr, u32 asid)
asm volatile (__ex(SVM_INVLPGA) :: "a"(addr), "c"(asid));
}
-static inline unsigned long kvm_read_cr2(void)
-{
- unsigned long cr2;
-
- asm volatile ("mov %%cr2, %0" : "=r" (cr2));
- return cr2;
-}
-
-static inline void kvm_write_cr2(unsigned long val)
-{
- asm volatile ("mov %0, %%cr2" :: "r" (val));
-}
-
static inline void force_new_asid(struct kvm_vcpu *vcpu)
{
to_svm(vcpu)->asid_generation--;
@@ -263,7 +321,7 @@ static void svm_hardware_enable(void *garbage)
struct svm_cpu_data *svm_data;
uint64_t efer;
- struct desc_ptr gdt_descr;
+ struct descriptor_table gdt_descr;
struct desc_struct *gdt;
int me = raw_smp_processor_id();
@@ -283,8 +341,8 @@ static void svm_hardware_enable(void *garbage)
svm_data->max_asid = cpuid_ebx(SVM_CPUID_FUNC) - 1;
svm_data->next_asid = svm_data->max_asid + 1;
- asm volatile ("sgdt %0" : "=m"(gdt_descr));
- gdt = (struct desc_struct *)gdt_descr.address;
+ kvm_get_gdt(&gdt_descr);
+ gdt = (struct desc_struct *)gdt_descr.base;
svm_data->tss_desc = (struct kvm_ldttss_desc *)(gdt + GDT_ENTRY_TSS);
rdmsrl(MSR_EFER, efer);
@@ -367,8 +425,6 @@ static void svm_vcpu_init_msrpm(u32 *msrpm)
#endif
set_msr_interception(msrpm, MSR_K6_STAR, 1, 1);
set_msr_interception(msrpm, MSR_IA32_SYSENTER_CS, 1, 1);
- set_msr_interception(msrpm, MSR_IA32_SYSENTER_ESP, 1, 1);
- set_msr_interception(msrpm, MSR_IA32_SYSENTER_EIP, 1, 1);
}
static void svm_enable_lbrv(struct vcpu_svm *svm)
@@ -595,8 +651,10 @@ static void init_vmcb(struct vcpu_svm *svm)
}
force_new_asid(&svm->vcpu);
- svm->nested_vmcb = 0;
- svm->vcpu.arch.hflags = HF_GIF_MASK;
+ svm->nested.vmcb = 0;
+ svm->vcpu.arch.hflags = 0;
+
+ enable_gif(svm);
}
static int svm_vcpu_reset(struct kvm_vcpu *vcpu)
@@ -605,7 +663,7 @@ static int svm_vcpu_reset(struct kvm_vcpu *vcpu)
init_vmcb(svm);
- if (vcpu->vcpu_id != 0) {
+ if (!kvm_vcpu_is_bsp(vcpu)) {
kvm_rip_write(vcpu, 0);
svm->vmcb->save.cs.base = svm->vcpu.arch.sipi_vector << 12;
svm->vmcb->save.cs.selector = svm->vcpu.arch.sipi_vector << 8;
@@ -656,9 +714,9 @@ static struct kvm_vcpu *svm_create_vcpu(struct kvm *kvm, unsigned int id)
hsave_page = alloc_page(GFP_KERNEL);
if (!hsave_page)
goto uninit;
- svm->hsave = page_address(hsave_page);
+ svm->nested.hsave = page_address(hsave_page);
- svm->nested_msrpm = page_address(nested_msrpm_pages);
+ svm->nested.msrpm = page_address(nested_msrpm_pages);
svm->vmcb = page_address(page);
clear_page(svm->vmcb);
@@ -669,7 +727,7 @@ static struct kvm_vcpu *svm_create_vcpu(struct kvm *kvm, unsigned int id)
fx_init(&svm->vcpu);
svm->vcpu.fpu_active = 1;
svm->vcpu.arch.apic_base = 0xfee00000 | MSR_IA32_APICBASE_ENABLE;
- if (svm->vcpu.vcpu_id == 0)
+ if (kvm_vcpu_is_bsp(&svm->vcpu))
svm->vcpu.arch.apic_base |= MSR_IA32_APICBASE_BSP;
return &svm->vcpu;
@@ -688,8 +746,8 @@ static void svm_free_vcpu(struct kvm_vcpu *vcpu)
__free_page(pfn_to_page(svm->vmcb_pa >> PAGE_SHIFT));
__free_pages(virt_to_page(svm->msrpm), MSRPM_ALLOC_ORDER);
- __free_page(virt_to_page(svm->hsave));
- __free_pages(virt_to_page(svm->nested_msrpm), MSRPM_ALLOC_ORDER);
+ __free_page(virt_to_page(svm->nested.hsave));
+ __free_pages(virt_to_page(svm->nested.msrpm), MSRPM_ALLOC_ORDER);
kvm_vcpu_uninit(vcpu);
kmem_cache_free(kvm_vcpu_cache, svm);
}
@@ -740,6 +798,18 @@ static void svm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags)
to_svm(vcpu)->vmcb->save.rflags = rflags;
}
+static void svm_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg)
+{
+ switch (reg) {
+ case VCPU_EXREG_PDPTR:
+ BUG_ON(!npt_enabled);
+ load_pdptrs(vcpu, vcpu->arch.cr3);
+ break;
+ default:
+ BUG();
+ }
+}
+
static void svm_set_vintr(struct vcpu_svm *svm)
{
svm->vmcb->control.intercept |= 1ULL << INTERCEPT_VINTR;
@@ -1061,7 +1131,6 @@ static unsigned long svm_get_dr(struct kvm_vcpu *vcpu, int dr)
val = 0;
}
- KVMTRACE_2D(DR_READ, vcpu, (u32)dr, (u32)val, handler);
return val;
}
@@ -1070,8 +1139,6 @@ static void svm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long value,
{
struct vcpu_svm *svm = to_svm(vcpu);
- KVMTRACE_2D(DR_WRITE, vcpu, (u32)dr, (u32)value, handler);
-
*exception = 0;
switch (dr) {
@@ -1119,25 +1186,9 @@ static int pf_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
fault_address = svm->vmcb->control.exit_info_2;
error_code = svm->vmcb->control.exit_info_1;
- if (!npt_enabled)
- KVMTRACE_3D(PAGE_FAULT, &svm->vcpu, error_code,
- (u32)fault_address, (u32)(fault_address >> 32),
- handler);
- else
- KVMTRACE_3D(TDP_FAULT, &svm->vcpu, error_code,
- (u32)fault_address, (u32)(fault_address >> 32),
- handler);
- /*
- * FIXME: Tis shouldn't be necessary here, but there is a flush
- * missing in the MMU code. Until we find this bug, flush the
- * complete TLB here on an NPF
- */
- if (npt_enabled)
- svm_flush_tlb(&svm->vcpu);
- else {
- if (kvm_event_needs_reinjection(&svm->vcpu))
- kvm_mmu_unprotect_page_virt(&svm->vcpu, fault_address);
- }
+ trace_kvm_page_fault(fault_address, error_code);
+ if (!npt_enabled && kvm_event_needs_reinjection(&svm->vcpu))
+ kvm_mmu_unprotect_page_virt(&svm->vcpu, fault_address);
return kvm_mmu_page_fault(&svm->vcpu, fault_address, error_code);
}
@@ -1253,14 +1304,12 @@ static int io_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
static int nmi_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
{
- KVMTRACE_0D(NMI, &svm->vcpu, handler);
return 1;
}
static int intr_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
{
++svm->vcpu.stat.irq_exits;
- KVMTRACE_0D(INTR, &svm->vcpu, handler);
return 1;
}
@@ -1303,44 +1352,39 @@ static int nested_svm_check_permissions(struct vcpu_svm *svm)
static int nested_svm_check_exception(struct vcpu_svm *svm, unsigned nr,
bool has_error_code, u32 error_code)
{
- if (is_nested(svm)) {
- svm->vmcb->control.exit_code = SVM_EXIT_EXCP_BASE + nr;
- svm->vmcb->control.exit_code_hi = 0;
- svm->vmcb->control.exit_info_1 = error_code;
- svm->vmcb->control.exit_info_2 = svm->vcpu.arch.cr2;
- if (nested_svm_exit_handled(svm, false)) {
- nsvm_printk("VMexit -> EXCP 0x%x\n", nr);
-
- nested_svm_vmexit(svm);
- return 1;
- }
- }
+ if (!is_nested(svm))
+ return 0;
- return 0;
+ svm->vmcb->control.exit_code = SVM_EXIT_EXCP_BASE + nr;
+ svm->vmcb->control.exit_code_hi = 0;
+ svm->vmcb->control.exit_info_1 = error_code;
+ svm->vmcb->control.exit_info_2 = svm->vcpu.arch.cr2;
+
+ return nested_svm_exit_handled(svm);
}
static inline int nested_svm_intr(struct vcpu_svm *svm)
{
- if (is_nested(svm)) {
- if (!(svm->vcpu.arch.hflags & HF_VINTR_MASK))
- return 0;
+ if (!is_nested(svm))
+ return 0;
- if (!(svm->vcpu.arch.hflags & HF_HIF_MASK))
- return 0;
+ if (!(svm->vcpu.arch.hflags & HF_VINTR_MASK))
+ return 0;
- svm->vmcb->control.exit_code = SVM_EXIT_INTR;
+ if (!(svm->vcpu.arch.hflags & HF_HIF_MASK))
+ return 0;
- if (nested_svm_exit_handled(svm, false)) {
- nsvm_printk("VMexit -> INTR\n");
- nested_svm_vmexit(svm);
- return 1;
- }
+ svm->vmcb->control.exit_code = SVM_EXIT_INTR;
+
+ if (nested_svm_exit_handled(svm)) {
+ nsvm_printk("VMexit -> INTR\n");
+ return 1;
}
return 0;
}
-static struct page *nested_svm_get_page(struct vcpu_svm *svm, u64 gpa)
+static void *nested_svm_map(struct vcpu_svm *svm, u64 gpa, enum km_type idx)
{
struct page *page;
@@ -1348,236 +1392,246 @@ static struct page *nested_svm_get_page(struct vcpu_svm *svm, u64 gpa)
page = gfn_to_page(svm->vcpu.kvm, gpa >> PAGE_SHIFT);
up_read(&current->mm->mmap_sem);
- if (is_error_page(page)) {
- printk(KERN_INFO "%s: could not find page at 0x%llx\n",
- __func__, gpa);
- kvm_release_page_clean(page);
- kvm_inject_gp(&svm->vcpu, 0);
- return NULL;
- }
- return page;
+ if (is_error_page(page))
+ goto error;
+
+ return kmap_atomic(page, idx);
+
+error:
+ kvm_release_page_clean(page);
+ kvm_inject_gp(&svm->vcpu, 0);
+
+ return NULL;
}
-static int nested_svm_do(struct vcpu_svm *svm,
- u64 arg1_gpa, u64 arg2_gpa, void *opaque,
- int (*handler)(struct vcpu_svm *svm,
- void *arg1,
- void *arg2,
- void *opaque))
+static void nested_svm_unmap(void *addr, enum km_type idx)
{
- struct page *arg1_page;
- struct page *arg2_page = NULL;
- void *arg1;
- void *arg2 = NULL;
- int retval;
+ struct page *page;
- arg1_page = nested_svm_get_page(svm, arg1_gpa);
- if(arg1_page == NULL)
- return 1;
+ if (!addr)
+ return;
- if (arg2_gpa) {
- arg2_page = nested_svm_get_page(svm, arg2_gpa);
- if(arg2_page == NULL) {
- kvm_release_page_clean(arg1_page);
- return 1;
- }
- }
+ page = kmap_atomic_to_page(addr);
+
+ kunmap_atomic(addr, idx);
+ kvm_release_page_dirty(page);
+}
+
+static bool nested_svm_exit_handled_msr(struct vcpu_svm *svm)
+{
+ u32 param = svm->vmcb->control.exit_info_1 & 1;
+ u32 msr = svm->vcpu.arch.regs[VCPU_REGS_RCX];
+ bool ret = false;
+ u32 t0, t1;
+ u8 *msrpm;
- arg1 = kmap_atomic(arg1_page, KM_USER0);
- if (arg2_gpa)
- arg2 = kmap_atomic(arg2_page, KM_USER1);
+ if (!(svm->nested.intercept & (1ULL << INTERCEPT_MSR_PROT)))
+ return false;
- retval = handler(svm, arg1, arg2, opaque);
+ msrpm = nested_svm_map(svm, svm->nested.vmcb_msrpm, KM_USER0);
+
+ if (!msrpm)
+ goto out;
+
+ switch (msr) {
+ case 0 ... 0x1fff:
+ t0 = (msr * 2) % 8;
+ t1 = msr / 8;
+ break;
+ case 0xc0000000 ... 0xc0001fff:
+ t0 = (8192 + msr - 0xc0000000) * 2;
+ t1 = (t0 / 8);
+ t0 %= 8;
+ break;
+ case 0xc0010000 ... 0xc0011fff:
+ t0 = (16384 + msr - 0xc0010000) * 2;
+ t1 = (t0 / 8);
+ t0 %= 8;
+ break;
+ default:
+ ret = true;
+ goto out;
+ }
- kunmap_atomic(arg1, KM_USER0);
- if (arg2_gpa)
- kunmap_atomic(arg2, KM_USER1);
+ ret = msrpm[t1] & ((1 << param) << t0);
- kvm_release_page_dirty(arg1_page);
- if (arg2_gpa)
- kvm_release_page_dirty(arg2_page);
+out:
+ nested_svm_unmap(msrpm, KM_USER0);
- return retval;
+ return ret;
}
-static int nested_svm_exit_handled_real(struct vcpu_svm *svm,
- void *arg1,
- void *arg2,
- void *opaque)
+static int nested_svm_exit_special(struct vcpu_svm *svm)
{
- struct vmcb *nested_vmcb = (struct vmcb *)arg1;
- bool kvm_overrides = *(bool *)opaque;
u32 exit_code = svm->vmcb->control.exit_code;
- if (kvm_overrides) {
- switch (exit_code) {
- case SVM_EXIT_INTR:
- case SVM_EXIT_NMI:
- return 0;
+ switch (exit_code) {
+ case SVM_EXIT_INTR:
+ case SVM_EXIT_NMI:
+ return NESTED_EXIT_HOST;
/* For now we are always handling NPFs when using them */
- case SVM_EXIT_NPF:
- if (npt_enabled)
- return 0;
- break;
- /* When we're shadowing, trap PFs */
- case SVM_EXIT_EXCP_BASE + PF_VECTOR:
- if (!npt_enabled)
- return 0;
- break;
- default:
- break;
- }
+ case SVM_EXIT_NPF:
+ if (npt_enabled)
+ return NESTED_EXIT_HOST;
+ break;
+ /* When we're shadowing, trap PFs */
+ case SVM_EXIT_EXCP_BASE + PF_VECTOR:
+ if (!npt_enabled)
+ return NESTED_EXIT_HOST;
+ break;
+ default:
+ break;
}
+ return NESTED_EXIT_CONTINUE;
+}
+
+/*
+ * If this function returns true, this #vmexit was already handled
+ */
+static int nested_svm_exit_handled(struct vcpu_svm *svm)
+{
+ u32 exit_code = svm->vmcb->control.exit_code;
+ int vmexit = NESTED_EXIT_HOST;
+
switch (exit_code) {
+ case SVM_EXIT_MSR:
+ vmexit = nested_svm_exit_handled_msr(svm);
+ break;
case SVM_EXIT_READ_CR0 ... SVM_EXIT_READ_CR8: {
u32 cr_bits = 1 << (exit_code - SVM_EXIT_READ_CR0);
- if (nested_vmcb->control.intercept_cr_read & cr_bits)
- return 1;
+ if (svm->nested.intercept_cr_read & cr_bits)
+ vmexit = NESTED_EXIT_DONE;
break;
}
case SVM_EXIT_WRITE_CR0 ... SVM_EXIT_WRITE_CR8: {
u32 cr_bits = 1 << (exit_code - SVM_EXIT_WRITE_CR0);
- if (nested_vmcb->control.intercept_cr_write & cr_bits)
- return 1;
+ if (svm->nested.intercept_cr_write & cr_bits)
+ vmexit = NESTED_EXIT_DONE;
break;
}
case SVM_EXIT_READ_DR0 ... SVM_EXIT_READ_DR7: {
u32 dr_bits = 1 << (exit_code - SVM_EXIT_READ_DR0);
- if (nested_vmcb->control.intercept_dr_read & dr_bits)
- return 1;
+ if (svm->nested.intercept_dr_read & dr_bits)
+ vmexit = NESTED_EXIT_DONE;
break;
}
case SVM_EXIT_WRITE_DR0 ... SVM_EXIT_WRITE_DR7: {
u32 dr_bits = 1 << (exit_code - SVM_EXIT_WRITE_DR0);
- if (nested_vmcb->control.intercept_dr_write & dr_bits)
- return 1;
+ if (svm->nested.intercept_dr_write & dr_bits)
+ vmexit = NESTED_EXIT_DONE;
break;
}
case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 0x1f: {
u32 excp_bits = 1 << (exit_code - SVM_EXIT_EXCP_BASE);
- if (nested_vmcb->control.intercept_exceptions & excp_bits)
- return 1;
+ if (svm->nested.intercept_exceptions & excp_bits)
+ vmexit = NESTED_EXIT_DONE;
break;
}
default: {
u64 exit_bits = 1ULL << (exit_code - SVM_EXIT_INTR);
nsvm_printk("exit code: 0x%x\n", exit_code);
- if (nested_vmcb->control.intercept & exit_bits)
- return 1;
+ if (svm->nested.intercept & exit_bits)
+ vmexit = NESTED_EXIT_DONE;
}
}
- return 0;
-}
-
-static int nested_svm_exit_handled_msr(struct vcpu_svm *svm,
- void *arg1, void *arg2,
- void *opaque)
-{
- struct vmcb *nested_vmcb = (struct vmcb *)arg1;
- u8 *msrpm = (u8 *)arg2;
- u32 t0, t1;
- u32 msr = svm->vcpu.arch.regs[VCPU_REGS_RCX];
- u32 param = svm->vmcb->control.exit_info_1 & 1;
-
- if (!(nested_vmcb->control.intercept & (1ULL << INTERCEPT_MSR_PROT)))
- return 0;
-
- switch(msr) {
- case 0 ... 0x1fff:
- t0 = (msr * 2) % 8;
- t1 = msr / 8;
- break;
- case 0xc0000000 ... 0xc0001fff:
- t0 = (8192 + msr - 0xc0000000) * 2;
- t1 = (t0 / 8);
- t0 %= 8;
- break;
- case 0xc0010000 ... 0xc0011fff:
- t0 = (16384 + msr - 0xc0010000) * 2;
- t1 = (t0 / 8);
- t0 %= 8;
- break;
- default:
- return 1;
- break;
+ if (vmexit == NESTED_EXIT_DONE) {
+ nsvm_printk("#VMEXIT reason=%04x\n", exit_code);
+ nested_svm_vmexit(svm);
}
- if (msrpm[t1] & ((1 << param) << t0))
- return 1;
- return 0;
+ return vmexit;
+}
+
+static inline void copy_vmcb_control_area(struct vmcb *dst_vmcb, struct vmcb *from_vmcb)
+{
+ struct vmcb_control_area *dst = &dst_vmcb->control;
+ struct vmcb_control_area *from = &from_vmcb->control;
+
+ dst->intercept_cr_read = from->intercept_cr_read;
+ dst->intercept_cr_write = from->intercept_cr_write;
+ dst->intercept_dr_read = from->intercept_dr_read;
+ dst->intercept_dr_write = from->intercept_dr_write;
+ dst->intercept_exceptions = from->intercept_exceptions;
+ dst->intercept = from->intercept;
+ dst->iopm_base_pa = from->iopm_base_pa;
+ dst->msrpm_base_pa = from->msrpm_base_pa;
+ dst->tsc_offset = from->tsc_offset;
+ dst->asid = from->asid;
+ dst->tlb_ctl = from->tlb_ctl;
+ dst->int_ctl = from->int_ctl;
+ dst->int_vector = from->int_vector;
+ dst->int_state = from->int_state;
+ dst->exit_code = from->exit_code;
+ dst->exit_code_hi = from->exit_code_hi;
+ dst->exit_info_1 = from->exit_info_1;
+ dst->exit_info_2 = from->exit_info_2;
+ dst->exit_int_info = from->exit_int_info;
+ dst->exit_int_info_err = from->exit_int_info_err;
+ dst->nested_ctl = from->nested_ctl;
+ dst->event_inj = from->event_inj;
+ dst->event_inj_err = from->event_inj_err;
+ dst->nested_cr3 = from->nested_cr3;
+ dst->lbr_ctl = from->lbr_ctl;
}
-static int nested_svm_exit_handled(struct vcpu_svm *svm, bool kvm_override)
+static int nested_svm_vmexit(struct vcpu_svm *svm)
{
- bool k = kvm_override;
-
- switch (svm->vmcb->control.exit_code) {
- case SVM_EXIT_MSR:
- return nested_svm_do(svm, svm->nested_vmcb,
- svm->nested_vmcb_msrpm, NULL,
- nested_svm_exit_handled_msr);
- default: break;
- }
+ struct vmcb *nested_vmcb;
+ struct vmcb *hsave = svm->nested.hsave;
+ struct vmcb *vmcb = svm->vmcb;
- return nested_svm_do(svm, svm->nested_vmcb, 0, &k,
- nested_svm_exit_handled_real);
-}
-
-static int nested_svm_vmexit_real(struct vcpu_svm *svm, void *arg1,
- void *arg2, void *opaque)
-{
- struct vmcb *nested_vmcb = (struct vmcb *)arg1;
- struct vmcb *hsave = svm->hsave;
- u64 nested_save[] = { nested_vmcb->save.cr0,
- nested_vmcb->save.cr3,
- nested_vmcb->save.cr4,
- nested_vmcb->save.efer,
- nested_vmcb->control.intercept_cr_read,
- nested_vmcb->control.intercept_cr_write,
- nested_vmcb->control.intercept_dr_read,
- nested_vmcb->control.intercept_dr_write,
- nested_vmcb->control.intercept_exceptions,
- nested_vmcb->control.intercept,
- nested_vmcb->control.msrpm_base_pa,
- nested_vmcb->control.iopm_base_pa,
- nested_vmcb->control.tsc_offset };
+ nested_vmcb = nested_svm_map(svm, svm->nested.vmcb, KM_USER0);
+ if (!nested_vmcb)
+ return 1;
/* Give the current vmcb to the guest */
- memcpy(nested_vmcb, svm->vmcb, sizeof(struct vmcb));
- nested_vmcb->save.cr0 = nested_save[0];
- if (!npt_enabled)
- nested_vmcb->save.cr3 = nested_save[1];
- nested_vmcb->save.cr4 = nested_save[2];
- nested_vmcb->save.efer = nested_save[3];
- nested_vmcb->control.intercept_cr_read = nested_save[4];
- nested_vmcb->control.intercept_cr_write = nested_save[5];
- nested_vmcb->control.intercept_dr_read = nested_save[6];
- nested_vmcb->control.intercept_dr_write = nested_save[7];
- nested_vmcb->control.intercept_exceptions = nested_save[8];
- nested_vmcb->control.intercept = nested_save[9];
- nested_vmcb->control.msrpm_base_pa = nested_save[10];
- nested_vmcb->control.iopm_base_pa = nested_save[11];
- nested_vmcb->control.tsc_offset = nested_save[12];
+ disable_gif(svm);
+
+ nested_vmcb->save.es = vmcb->save.es;
+ nested_vmcb->save.cs = vmcb->save.cs;
+ nested_vmcb->save.ss = vmcb->save.ss;
+ nested_vmcb->save.ds = vmcb->save.ds;
+ nested_vmcb->save.gdtr = vmcb->save.gdtr;
+ nested_vmcb->save.idtr = vmcb->save.idtr;
+ if (npt_enabled)
+ nested_vmcb->save.cr3 = vmcb->save.cr3;
+ nested_vmcb->save.cr2 = vmcb->save.cr2;
+ nested_vmcb->save.rflags = vmcb->save.rflags;
+ nested_vmcb->save.rip = vmcb->save.rip;
+ nested_vmcb->save.rsp = vmcb->save.rsp;
+ nested_vmcb->save.rax = vmcb->save.rax;
+ nested_vmcb->save.dr7 = vmcb->save.dr7;
+ nested_vmcb->save.dr6 = vmcb->save.dr6;
+ nested_vmcb->save.cpl = vmcb->save.cpl;
+
+ nested_vmcb->control.int_ctl = vmcb->control.int_ctl;
+ nested_vmcb->control.int_vector = vmcb->control.int_vector;
+ nested_vmcb->control.int_state = vmcb->control.int_state;
+ nested_vmcb->control.exit_code = vmcb->control.exit_code;
+ nested_vmcb->control.exit_code_hi = vmcb->control.exit_code_hi;
+ nested_vmcb->control.exit_info_1 = vmcb->control.exit_info_1;
+ nested_vmcb->control.exit_info_2 = vmcb->control.exit_info_2;
+ nested_vmcb->control.exit_int_info = vmcb->control.exit_int_info;
+ nested_vmcb->control.exit_int_info_err = vmcb->control.exit_int_info_err;
+ nested_vmcb->control.tlb_ctl = 0;
+ nested_vmcb->control.event_inj = 0;
+ nested_vmcb->control.event_inj_err = 0;
/* We always set V_INTR_MASKING and remember the old value in hflags */
if (!(svm->vcpu.arch.hflags & HF_VINTR_MASK))
nested_vmcb->control.int_ctl &= ~V_INTR_MASKING_MASK;
- if ((nested_vmcb->control.int_ctl & V_IRQ_MASK) &&
- (nested_vmcb->control.int_vector)) {
- nsvm_printk("WARNING: IRQ 0x%x still enabled on #VMEXIT\n",
- nested_vmcb->control.int_vector);
- }
-
/* Restore the original control entries */
- svm->vmcb->control = hsave->control;
+ copy_vmcb_control_area(vmcb, hsave);
/* Kill any pending exceptions */
if (svm->vcpu.arch.exception.pending == true)
nsvm_printk("WARNING: Pending Exception\n");
- svm->vcpu.arch.exception.pending = false;
+
+ kvm_clear_exception_queue(&svm->vcpu);
+ kvm_clear_interrupt_queue(&svm->vcpu);
/* Restore selected save entries */
svm->vmcb->save.es = hsave->save.es;
@@ -1603,19 +1657,10 @@ static int nested_svm_vmexit_real(struct vcpu_svm *svm, void *arg1,
svm->vmcb->save.cpl = 0;
svm->vmcb->control.exit_int_info = 0;
- svm->vcpu.arch.hflags &= ~HF_GIF_MASK;
/* Exit nested SVM mode */
- svm->nested_vmcb = 0;
+ svm->nested.vmcb = 0;
- return 0;
-}
-
-static int nested_svm_vmexit(struct vcpu_svm *svm)
-{
- nsvm_printk("VMexit\n");
- if (nested_svm_do(svm, svm->nested_vmcb, 0,
- NULL, nested_svm_vmexit_real))
- return 1;
+ nested_svm_unmap(nested_vmcb, KM_USER0);
kvm_mmu_reset_context(&svm->vcpu);
kvm_mmu_load(&svm->vcpu);
@@ -1623,38 +1668,63 @@ static int nested_svm_vmexit(struct vcpu_svm *svm)
return 0;
}
-static int nested_svm_vmrun_msrpm(struct vcpu_svm *svm, void *arg1,
- void *arg2, void *opaque)
+static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm)
{
+ u32 *nested_msrpm;
int i;
- u32 *nested_msrpm = (u32*)arg1;
+
+ nested_msrpm = nested_svm_map(svm, svm->nested.vmcb_msrpm, KM_USER0);
+ if (!nested_msrpm)
+ return false;
+
for (i=0; i< PAGE_SIZE * (1 << MSRPM_ALLOC_ORDER) / 4; i++)
- svm->nested_msrpm[i] = svm->msrpm[i] | nested_msrpm[i];
- svm->vmcb->control.msrpm_base_pa = __pa(svm->nested_msrpm);
+ svm->nested.msrpm[i] = svm->msrpm[i] | nested_msrpm[i];
- return 0;
+ svm->vmcb->control.msrpm_base_pa = __pa(svm->nested.msrpm);
+
+ nested_svm_unmap(nested_msrpm, KM_USER0);
+
+ return true;
}
-static int nested_svm_vmrun(struct vcpu_svm *svm, void *arg1,
- void *arg2, void *opaque)
+static bool nested_svm_vmrun(struct vcpu_svm *svm)
{
- struct vmcb *nested_vmcb = (struct vmcb *)arg1;
- struct vmcb *hsave = svm->hsave;
+ struct vmcb *nested_vmcb;
+ struct vmcb *hsave = svm->nested.hsave;
+ struct vmcb *vmcb = svm->vmcb;
+
+ nested_vmcb = nested_svm_map(svm, svm->vmcb->save.rax, KM_USER0);
+ if (!nested_vmcb)
+ return false;
/* nested_vmcb is our indicator if nested SVM is activated */
- svm->nested_vmcb = svm->vmcb->save.rax;
+ svm->nested.vmcb = svm->vmcb->save.rax;
/* Clear internal status */
- svm->vcpu.arch.exception.pending = false;
+ kvm_clear_exception_queue(&svm->vcpu);
+ kvm_clear_interrupt_queue(&svm->vcpu);
/* Save the old vmcb, so we don't need to pick what we save, but
can restore everything when a VMEXIT occurs */
- memcpy(hsave, svm->vmcb, sizeof(struct vmcb));
- /* We need to remember the original CR3 in the SPT case */
- if (!npt_enabled)
- hsave->save.cr3 = svm->vcpu.arch.cr3;
- hsave->save.cr4 = svm->vcpu.arch.cr4;
- hsave->save.rip = svm->next_rip;
+ hsave->save.es = vmcb->save.es;
+ hsave->save.cs = vmcb->save.cs;
+ hsave->save.ss = vmcb->save.ss;
+ hsave->save.ds = vmcb->save.ds;
+ hsave->save.gdtr = vmcb->save.gdtr;
+ hsave->save.idtr = vmcb->save.idtr;
+ hsave->save.efer = svm->vcpu.arch.shadow_efer;
+ hsave->save.cr0 = svm->vcpu.arch.cr0;
+ hsave->save.cr4 = svm->vcpu.arch.cr4;
+ hsave->save.rflags = vmcb->save.rflags;
+ hsave->save.rip = svm->next_rip;
+ hsave->save.rsp = vmcb->save.rsp;
+ hsave->save.rax = vmcb->save.rax;
+ if (npt_enabled)
+ hsave->save.cr3 = vmcb->save.cr3;
+ else
+ hsave->save.cr3 = svm->vcpu.arch.cr3;
+
+ copy_vmcb_control_area(hsave, vmcb);
if (svm->vmcb->save.rflags & X86_EFLAGS_IF)
svm->vcpu.arch.hflags |= HF_HIF_MASK;
@@ -1679,7 +1749,7 @@ static int nested_svm_vmrun(struct vcpu_svm *svm, void *arg1,
kvm_set_cr3(&svm->vcpu, nested_vmcb->save.cr3);
kvm_mmu_reset_context(&svm->vcpu);
}
- svm->vmcb->save.cr2 = nested_vmcb->save.cr2;
+ svm->vmcb->save.cr2 = svm->vcpu.arch.cr2 = nested_vmcb->save.cr2;
kvm_register_write(&svm->vcpu, VCPU_REGS_RAX, nested_vmcb->save.rax);
kvm_register_write(&svm->vcpu, VCPU_REGS_RSP, nested_vmcb->save.rsp);
kvm_register_write(&svm->vcpu, VCPU_REGS_RIP, nested_vmcb->save.rip);
@@ -1706,7 +1776,15 @@ static int nested_svm_vmrun(struct vcpu_svm *svm, void *arg1,
svm->vmcb->control.intercept |= nested_vmcb->control.intercept;
- svm->nested_vmcb_msrpm = nested_vmcb->control.msrpm_base_pa;
+ svm->nested.vmcb_msrpm = nested_vmcb->control.msrpm_base_pa;
+
+ /* cache intercepts */
+ svm->nested.intercept_cr_read = nested_vmcb->control.intercept_cr_read;
+ svm->nested.intercept_cr_write = nested_vmcb->control.intercept_cr_write;
+ svm->nested.intercept_dr_read = nested_vmcb->control.intercept_dr_read;
+ svm->nested.intercept_dr_write = nested_vmcb->control.intercept_dr_write;
+ svm->nested.intercept_exceptions = nested_vmcb->control.intercept_exceptions;
+ svm->nested.intercept = nested_vmcb->control.intercept;
force_new_asid(&svm->vcpu);
svm->vmcb->control.exit_int_info = nested_vmcb->control.exit_int_info;
@@ -1734,12 +1812,14 @@ static int nested_svm_vmrun(struct vcpu_svm *svm, void *arg1,
svm->vmcb->control.event_inj = nested_vmcb->control.event_inj;
svm->vmcb->control.event_inj_err = nested_vmcb->control.event_inj_err;
- svm->vcpu.arch.hflags |= HF_GIF_MASK;
+ nested_svm_unmap(nested_vmcb, KM_USER0);
- return 0;
+ enable_gif(svm);
+
+ return true;
}
-static int nested_svm_vmloadsave(struct vmcb *from_vmcb, struct vmcb *to_vmcb)
+static void nested_svm_vmloadsave(struct vmcb *from_vmcb, struct vmcb *to_vmcb)
{
to_vmcb->save.fs = from_vmcb->save.fs;
to_vmcb->save.gs = from_vmcb->save.gs;
@@ -1753,44 +1833,44 @@ static int nested_svm_vmloadsave(struct vmcb *from_vmcb, struct vmcb *to_vmcb)
to_vmcb->save.sysenter_cs = from_vmcb->save.sysenter_cs;
to_vmcb->save.sysenter_esp = from_vmcb->save.sysenter_esp;
to_vmcb->save.sysenter_eip = from_vmcb->save.sysenter_eip;
-
- return 1;
-}
-
-static int nested_svm_vmload(struct vcpu_svm *svm, void *nested_vmcb,
- void *arg2, void *opaque)
-{
- return nested_svm_vmloadsave((struct vmcb *)nested_vmcb, svm->vmcb);
-}
-
-static int nested_svm_vmsave(struct vcpu_svm *svm, void *nested_vmcb,
- void *arg2, void *opaque)
-{
- return nested_svm_vmloadsave(svm->vmcb, (struct vmcb *)nested_vmcb);
}
static int vmload_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
{
+ struct vmcb *nested_vmcb;
+
if (nested_svm_check_permissions(svm))
return 1;
svm->next_rip = kvm_rip_read(&svm->vcpu) + 3;
skip_emulated_instruction(&svm->vcpu);
- nested_svm_do(svm, svm->vmcb->save.rax, 0, NULL, nested_svm_vmload);
+ nested_vmcb = nested_svm_map(svm, svm->vmcb->save.rax, KM_USER0);
+ if (!nested_vmcb)
+ return 1;
+
+ nested_svm_vmloadsave(nested_vmcb, svm->vmcb);
+ nested_svm_unmap(nested_vmcb, KM_USER0);
return 1;
}
static int vmsave_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
{
+ struct vmcb *nested_vmcb;
+
if (nested_svm_check_permissions(svm))
return 1;
svm->next_rip = kvm_rip_read(&svm->vcpu) + 3;
skip_emulated_instruction(&svm->vcpu);
- nested_svm_do(svm, svm->vmcb->save.rax, 0, NULL, nested_svm_vmsave);
+ nested_vmcb = nested_svm_map(svm, svm->vmcb->save.rax, KM_USER0);
+ if (!nested_vmcb)
+ return 1;
+
+ nested_svm_vmloadsave(svm->vmcb, nested_vmcb);
+ nested_svm_unmap(nested_vmcb, KM_USER0);
return 1;
}
@@ -1798,19 +1878,29 @@ static int vmsave_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
static int vmrun_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
{
nsvm_printk("VMrun\n");
+
if (nested_svm_check_permissions(svm))
return 1;
svm->next_rip = kvm_rip_read(&svm->vcpu) + 3;
skip_emulated_instruction(&svm->vcpu);
- if (nested_svm_do(svm, svm->vmcb->save.rax, 0,
- NULL, nested_svm_vmrun))
+ if (!nested_svm_vmrun(svm))
return 1;
- if (nested_svm_do(svm, svm->nested_vmcb_msrpm, 0,
- NULL, nested_svm_vmrun_msrpm))
- return 1;
+ if (!nested_svm_vmrun_msrpm(svm))
+ goto failed;
+
+ return 1;
+
+failed:
+
+ svm->vmcb->control.exit_code = SVM_EXIT_ERR;
+ svm->vmcb->control.exit_code_hi = 0;
+ svm->vmcb->control.exit_info_1 = 0;
+ svm->vmcb->control.exit_info_2 = 0;
+
+ nested_svm_vmexit(svm);
return 1;
}
@@ -1823,7 +1913,7 @@ static int stgi_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
svm->next_rip = kvm_rip_read(&svm->vcpu) + 3;
skip_emulated_instruction(&svm->vcpu);
- svm->vcpu.arch.hflags |= HF_GIF_MASK;
+ enable_gif(svm);
return 1;
}
@@ -1836,7 +1926,7 @@ static int clgi_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
svm->next_rip = kvm_rip_read(&svm->vcpu) + 3;
skip_emulated_instruction(&svm->vcpu);
- svm->vcpu.arch.hflags &= ~HF_GIF_MASK;
+ disable_gif(svm);
/* After a CLGI no interrupts should come */
svm_clear_vintr(svm);
@@ -1845,6 +1935,19 @@ static int clgi_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
return 1;
}
+static int invlpga_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
+{
+ struct kvm_vcpu *vcpu = &svm->vcpu;
+ nsvm_printk("INVLPGA\n");
+
+ /* Let's treat INVLPGA the same as INVLPG (can be optimized!) */
+ kvm_mmu_invlpg(vcpu, vcpu->arch.regs[VCPU_REGS_RAX]);
+
+ svm->next_rip = kvm_rip_read(&svm->vcpu) + 3;
+ skip_emulated_instruction(&svm->vcpu);
+ return 1;
+}
+
static int invalid_op_interception(struct vcpu_svm *svm,
struct kvm_run *kvm_run)
{
@@ -1953,7 +2056,7 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 *data)
struct vcpu_svm *svm = to_svm(vcpu);
switch (ecx) {
- case MSR_IA32_TIME_STAMP_COUNTER: {
+ case MSR_IA32_TSC: {
u64 tsc;
rdtscll(tsc);
@@ -1981,10 +2084,10 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 *data)
*data = svm->vmcb->save.sysenter_cs;
break;
case MSR_IA32_SYSENTER_EIP:
- *data = svm->vmcb->save.sysenter_eip;
+ *data = svm->sysenter_eip;
break;
case MSR_IA32_SYSENTER_ESP:
- *data = svm->vmcb->save.sysenter_esp;
+ *data = svm->sysenter_esp;
break;
/* Nobody will change the following 5 values in the VMCB so
we can safely return them on rdmsr. They will always be 0
@@ -2005,7 +2108,7 @@ static int svm_get_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 *data)
*data = svm->vmcb->save.last_excp_to;
break;
case MSR_VM_HSAVE_PA:
- *data = svm->hsave_msr;
+ *data = svm->nested.hsave_msr;
break;
case MSR_VM_CR:
*data = 0;
@@ -2027,8 +2130,7 @@ static int rdmsr_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
if (svm_get_msr(&svm->vcpu, ecx, &data))
kvm_inject_gp(&svm->vcpu, 0);
else {
- KVMTRACE_3D(MSR_READ, &svm->vcpu, ecx, (u32)data,
- (u32)(data >> 32), handler);
+ trace_kvm_msr_read(ecx, data);
svm->vcpu.arch.regs[VCPU_REGS_RAX] = data & 0xffffffff;
svm->vcpu.arch.regs[VCPU_REGS_RDX] = data >> 32;
@@ -2043,7 +2145,7 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 data)
struct vcpu_svm *svm = to_svm(vcpu);
switch (ecx) {
- case MSR_IA32_TIME_STAMP_COUNTER: {
+ case MSR_IA32_TSC: {
u64 tsc;
rdtscll(tsc);
@@ -2071,9 +2173,11 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 data)
svm->vmcb->save.sysenter_cs = data;
break;
case MSR_IA32_SYSENTER_EIP:
+ svm->sysenter_eip = data;
svm->vmcb->save.sysenter_eip = data;
break;
case MSR_IA32_SYSENTER_ESP:
+ svm->sysenter_esp = data;
svm->vmcb->save.sysenter_esp = data;
break;
case MSR_IA32_DEBUGCTLMSR:
@@ -2091,24 +2195,12 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 data)
else
svm_disable_lbrv(svm);
break;
- case MSR_K7_EVNTSEL0:
- case MSR_K7_EVNTSEL1:
- case MSR_K7_EVNTSEL2:
- case MSR_K7_EVNTSEL3:
- case MSR_K7_PERFCTR0:
- case MSR_K7_PERFCTR1:
- case MSR_K7_PERFCTR2:
- case MSR_K7_PERFCTR3:
- /*
- * Just discard all writes to the performance counters; this
- * should keep both older linux and windows 64-bit guests
- * happy
- */
- pr_unimpl(vcpu, "unimplemented perfctr wrmsr: 0x%x data 0x%llx\n", ecx, data);
-
- break;
case MSR_VM_HSAVE_PA:
- svm->hsave_msr = data;
+ svm->nested.hsave_msr = data;
+ break;
+ case MSR_VM_CR:
+ case MSR_VM_IGNNE:
+ pr_unimpl(vcpu, "unimplemented wrmsr: 0x%x data 0x%llx\n", ecx, data);
break;
default:
return kvm_set_msr_common(vcpu, ecx, data);
@@ -2122,8 +2214,7 @@ static int wrmsr_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
u64 data = (svm->vcpu.arch.regs[VCPU_REGS_RAX] & -1u)
| ((u64)(svm->vcpu.arch.regs[VCPU_REGS_RDX] & -1u) << 32);
- KVMTRACE_3D(MSR_WRITE, &svm->vcpu, ecx, (u32)data, (u32)(data >> 32),
- handler);
+ trace_kvm_msr_write(ecx, data);
svm->next_rip = kvm_rip_read(&svm->vcpu) + 2;
if (svm_set_msr(&svm->vcpu, ecx, data))
@@ -2144,8 +2235,6 @@ static int msr_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
static int interrupt_window_interception(struct vcpu_svm *svm,
struct kvm_run *kvm_run)
{
- KVMTRACE_0D(PEND_INTR, &svm->vcpu, handler);
-
svm_clear_vintr(svm);
svm->vmcb->control.int_ctl &= ~V_IRQ_MASK;
/*
@@ -2201,7 +2290,7 @@ static int (*svm_exit_handlers[])(struct vcpu_svm *svm,
[SVM_EXIT_INVD] = emulate_on_interception,
[SVM_EXIT_HLT] = halt_interception,
[SVM_EXIT_INVLPG] = invlpg_interception,
- [SVM_EXIT_INVLPGA] = invalid_op_interception,
+ [SVM_EXIT_INVLPGA] = invlpga_interception,
[SVM_EXIT_IOIO] = io_interception,
[SVM_EXIT_MSR] = msr_interception,
[SVM_EXIT_TASK_SWITCH] = task_switch_interception,
@@ -2224,20 +2313,26 @@ static int handle_exit(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
struct vcpu_svm *svm = to_svm(vcpu);
u32 exit_code = svm->vmcb->control.exit_code;
- KVMTRACE_3D(VMEXIT, vcpu, exit_code, (u32)svm->vmcb->save.rip,
- (u32)((u64)svm->vmcb->save.rip >> 32), entryexit);
+ trace_kvm_exit(exit_code, svm->vmcb->save.rip);
if (is_nested(svm)) {
+ int vmexit;
+
nsvm_printk("nested handle_exit: 0x%x | 0x%lx | 0x%lx | 0x%lx\n",
exit_code, svm->vmcb->control.exit_info_1,
svm->vmcb->control.exit_info_2, svm->vmcb->save.rip);
- if (nested_svm_exit_handled(svm, true)) {
- nested_svm_vmexit(svm);
- nsvm_printk("-> #VMEXIT\n");
+
+ vmexit = nested_svm_exit_special(svm);
+
+ if (vmexit == NESTED_EXIT_CONTINUE)
+ vmexit = nested_svm_exit_handled(svm);
+
+ if (vmexit == NESTED_EXIT_DONE)
return 1;
- }
}
+ svm_complete_interrupts(svm);
+
if (npt_enabled) {
int mmu_reload = 0;
if ((vcpu->arch.cr0 ^ svm->vmcb->save.cr0) & X86_CR0_PG) {
@@ -2246,12 +2341,6 @@ static int handle_exit(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
}
vcpu->arch.cr0 = svm->vmcb->save.cr0;
vcpu->arch.cr3 = svm->vmcb->save.cr3;
- if (is_paging(vcpu) && is_pae(vcpu) && !is_long_mode(vcpu)) {
- if (!load_pdptrs(vcpu, vcpu->arch.cr3)) {
- kvm_inject_gp(vcpu, 0);
- return 1;
- }
- }
if (mmu_reload) {
kvm_mmu_reset_context(vcpu);
kvm_mmu_load(vcpu);
@@ -2319,7 +2408,7 @@ static inline void svm_inject_irq(struct vcpu_svm *svm, int irq)
{
struct vmcb_control_area *control;
- KVMTRACE_1D(INJ_VIRQ, &svm->vcpu, (u32)irq, handler);
+ trace_kvm_inj_virq(irq);
++svm->vcpu.stat.irq_injections;
control = &svm->vmcb->control;
@@ -2329,21 +2418,14 @@ static inline void svm_inject_irq(struct vcpu_svm *svm, int irq)
((/*control->int_vector >> 4*/ 0xf) << V_INTR_PRIO_SHIFT);
}
-static void svm_queue_irq(struct kvm_vcpu *vcpu, unsigned nr)
-{
- struct vcpu_svm *svm = to_svm(vcpu);
-
- svm->vmcb->control.event_inj = nr |
- SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_INTR;
-}
-
static void svm_set_irq(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
- nested_svm_intr(svm);
+ BUG_ON(!(gif_set(svm)));
- svm_queue_irq(vcpu, vcpu->arch.interrupt.nr);
+ svm->vmcb->control.event_inj = vcpu->arch.interrupt.nr |
+ SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_INTR;
}
static void update_cr8_intercept(struct kvm_vcpu *vcpu, int tpr, int irr)
@@ -2371,13 +2453,25 @@ static int svm_interrupt_allowed(struct kvm_vcpu *vcpu)
struct vmcb *vmcb = svm->vmcb;
return (vmcb->save.rflags & X86_EFLAGS_IF) &&
!(vmcb->control.int_state & SVM_INTERRUPT_SHADOW_MASK) &&
- (svm->vcpu.arch.hflags & HF_GIF_MASK);
+ gif_set(svm) &&
+ !(is_nested(svm) && (svm->vcpu.arch.hflags & HF_VINTR_MASK));
}
static void enable_irq_window(struct kvm_vcpu *vcpu)
{
- svm_set_vintr(to_svm(vcpu));
- svm_inject_irq(to_svm(vcpu), 0x0);
+ struct vcpu_svm *svm = to_svm(vcpu);
+ nsvm_printk("Trying to open IRQ window\n");
+
+ nested_svm_intr(svm);
+
+ /* In case GIF=0 we can't rely on the CPU to tell us when
+ * GIF becomes 1, because that's a separate STGI/VMRUN intercept.
+ * The next time we get that intercept, this function will be
+ * called again though and we'll get the vintr intercept. */
+ if (gif_set(svm)) {
+ svm_set_vintr(svm);
+ svm_inject_irq(svm, 0x0);
+ }
}
static void enable_nmi_window(struct kvm_vcpu *vcpu)
@@ -2456,6 +2550,8 @@ static void svm_complete_interrupts(struct vcpu_svm *svm)
case SVM_EXITINTINFO_TYPE_EXEPT:
/* In case of software exception do not reinject an exception
vector, but re-execute and instruction instead */
+ if (is_nested(svm))
+ break;
if (kvm_exception_is_soft(vector))
break;
if (exitintinfo & SVM_EXITINTINFO_VALID_ERR) {
@@ -2498,9 +2594,7 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
fs_selector = kvm_read_fs();
gs_selector = kvm_read_gs();
ldt_selector = kvm_read_ldt();
- svm->host_cr2 = kvm_read_cr2();
- if (!is_nested(svm))
- svm->vmcb->save.cr2 = vcpu->arch.cr2;
+ svm->vmcb->save.cr2 = vcpu->arch.cr2;
/* required for live migration with NPT */
if (npt_enabled)
svm->vmcb->save.cr3 = vcpu->arch.cr3;
@@ -2585,8 +2679,6 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
vcpu->arch.regs[VCPU_REGS_RSP] = svm->vmcb->save.rsp;
vcpu->arch.regs[VCPU_REGS_RIP] = svm->vmcb->save.rip;
- kvm_write_cr2(svm->host_cr2);
-
kvm_load_fs(fs_selector);
kvm_load_gs(gs_selector);
kvm_load_ldt(ldt_selector);
@@ -2602,7 +2694,10 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
svm->next_rip = 0;
- svm_complete_interrupts(svm);
+ if (npt_enabled) {
+ vcpu->arch.regs_avail &= ~(1 << VCPU_EXREG_PDPTR);
+ vcpu->arch.regs_dirty &= ~(1 << VCPU_EXREG_PDPTR);
+ }
}
#undef R
@@ -2673,6 +2768,64 @@ static u64 svm_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)
return 0;
}
+static const struct trace_print_flags svm_exit_reasons_str[] = {
+ { SVM_EXIT_READ_CR0, "read_cr0" },
+ { SVM_EXIT_READ_CR3, "read_cr3" },
+ { SVM_EXIT_READ_CR4, "read_cr4" },
+ { SVM_EXIT_READ_CR8, "read_cr8" },
+ { SVM_EXIT_WRITE_CR0, "write_cr0" },
+ { SVM_EXIT_WRITE_CR3, "write_cr3" },
+ { SVM_EXIT_WRITE_CR4, "write_cr4" },
+ { SVM_EXIT_WRITE_CR8, "write_cr8" },
+ { SVM_EXIT_READ_DR0, "read_dr0" },
+ { SVM_EXIT_READ_DR1, "read_dr1" },
+ { SVM_EXIT_READ_DR2, "read_dr2" },
+ { SVM_EXIT_READ_DR3, "read_dr3" },
+ { SVM_EXIT_WRITE_DR0, "write_dr0" },
+ { SVM_EXIT_WRITE_DR1, "write_dr1" },
+ { SVM_EXIT_WRITE_DR2, "write_dr2" },
+ { SVM_EXIT_WRITE_DR3, "write_dr3" },
+ { SVM_EXIT_WRITE_DR5, "write_dr5" },
+ { SVM_EXIT_WRITE_DR7, "write_dr7" },
+ { SVM_EXIT_EXCP_BASE + DB_VECTOR, "DB excp" },
+ { SVM_EXIT_EXCP_BASE + BP_VECTOR, "BP excp" },
+ { SVM_EXIT_EXCP_BASE + UD_VECTOR, "UD excp" },
+ { SVM_EXIT_EXCP_BASE + PF_VECTOR, "PF excp" },
+ { SVM_EXIT_EXCP_BASE + NM_VECTOR, "NM excp" },
+ { SVM_EXIT_EXCP_BASE + MC_VECTOR, "MC excp" },
+ { SVM_EXIT_INTR, "interrupt" },
+ { SVM_EXIT_NMI, "nmi" },
+ { SVM_EXIT_SMI, "smi" },
+ { SVM_EXIT_INIT, "init" },
+ { SVM_EXIT_VINTR, "vintr" },
+ { SVM_EXIT_CPUID, "cpuid" },
+ { SVM_EXIT_INVD, "invd" },
+ { SVM_EXIT_HLT, "hlt" },
+ { SVM_EXIT_INVLPG, "invlpg" },
+ { SVM_EXIT_INVLPGA, "invlpga" },
+ { SVM_EXIT_IOIO, "io" },
+ { SVM_EXIT_MSR, "msr" },
+ { SVM_EXIT_TASK_SWITCH, "task_switch" },
+ { SVM_EXIT_SHUTDOWN, "shutdown" },
+ { SVM_EXIT_VMRUN, "vmrun" },
+ { SVM_EXIT_VMMCALL, "hypercall" },
+ { SVM_EXIT_VMLOAD, "vmload" },
+ { SVM_EXIT_VMSAVE, "vmsave" },
+ { SVM_EXIT_STGI, "stgi" },
+ { SVM_EXIT_CLGI, "clgi" },
+ { SVM_EXIT_SKINIT, "skinit" },
+ { SVM_EXIT_WBINVD, "wbinvd" },
+ { SVM_EXIT_MONITOR, "monitor" },
+ { SVM_EXIT_MWAIT, "mwait" },
+ { SVM_EXIT_NPF, "npf" },
+ { -1, NULL }
+};
+
+static bool svm_gb_page_enable(void)
+{
+ return true;
+}
+
static struct kvm_x86_ops svm_x86_ops = {
.cpu_has_kvm_support = has_svm,
.disabled_by_bios = is_disabled,
@@ -2710,6 +2863,7 @@ static struct kvm_x86_ops svm_x86_ops = {
.set_gdt = svm_set_gdt,
.get_dr = svm_get_dr,
.set_dr = svm_set_dr,
+ .cache_reg = svm_cache_reg,
.get_rflags = svm_get_rflags,
.set_rflags = svm_set_rflags,
@@ -2733,6 +2887,9 @@ static struct kvm_x86_ops svm_x86_ops = {
.set_tss_addr = svm_set_tss_addr,
.get_tdp_level = get_npt_level,
.get_mt_mask = svm_get_mt_mask,
+
+ .exit_reasons_str = svm_exit_reasons_str,
+ .gb_page_enable = svm_gb_page_enable,
};
static int __init svm_init(void)
diff --git a/arch/x86/kvm/timer.c b/arch/x86/kvm/timer.c
index 86dbac072d0..eea40439066 100644
--- a/arch/x86/kvm/timer.c
+++ b/arch/x86/kvm/timer.c
@@ -9,12 +9,16 @@ static int __kvm_timer_fn(struct kvm_vcpu *vcpu, struct kvm_timer *ktimer)
int restart_timer = 0;
wait_queue_head_t *q = &vcpu->wq;
- /* FIXME: this code should not know anything about vcpus */
- if (!atomic_inc_and_test(&ktimer->pending))
+ /*
+ * There is a race window between reading and incrementing, but we do
+ * not care about potentially loosing timer events in the !reinject
+ * case anyway.
+ */
+ if (ktimer->reinject || !atomic_read(&ktimer->pending)) {
+ atomic_inc(&ktimer->pending);
+ /* FIXME: this code should not know anything about vcpus */
set_bit(KVM_REQ_PENDING_TIMER, &vcpu->requests);
-
- if (!ktimer->reinject)
- atomic_set(&ktimer->pending, 1);
+ }
if (waitqueue_active(q))
wake_up_interruptible(q);
@@ -33,7 +37,7 @@ enum hrtimer_restart kvm_timer_fn(struct hrtimer *data)
struct kvm_vcpu *vcpu;
struct kvm_timer *ktimer = container_of(data, struct kvm_timer, timer);
- vcpu = ktimer->kvm->vcpus[ktimer->vcpu_id];
+ vcpu = ktimer->vcpu;
if (!vcpu)
return HRTIMER_NORESTART;
diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h
new file mode 100644
index 00000000000..0d480e77eac
--- /dev/null
+++ b/arch/x86/kvm/trace.h
@@ -0,0 +1,355 @@
+#if !defined(_TRACE_KVM_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_KVM_H
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM kvm
+#define TRACE_INCLUDE_PATH arch/x86/kvm
+#define TRACE_INCLUDE_FILE trace
+
+/*
+ * Tracepoint for guest mode entry.
+ */
+TRACE_EVENT(kvm_entry,
+ TP_PROTO(unsigned int vcpu_id),
+ TP_ARGS(vcpu_id),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, vcpu_id )
+ ),
+
+ TP_fast_assign(
+ __entry->vcpu_id = vcpu_id;
+ ),
+
+ TP_printk("vcpu %u", __entry->vcpu_id)
+);
+
+/*
+ * Tracepoint for hypercall.
+ */
+TRACE_EVENT(kvm_hypercall,
+ TP_PROTO(unsigned long nr, unsigned long a0, unsigned long a1,
+ unsigned long a2, unsigned long a3),
+ TP_ARGS(nr, a0, a1, a2, a3),
+
+ TP_STRUCT__entry(
+ __field( unsigned long, nr )
+ __field( unsigned long, a0 )
+ __field( unsigned long, a1 )
+ __field( unsigned long, a2 )
+ __field( unsigned long, a3 )
+ ),
+
+ TP_fast_assign(
+ __entry->nr = nr;
+ __entry->a0 = a0;
+ __entry->a1 = a1;
+ __entry->a2 = a2;
+ __entry->a3 = a3;
+ ),
+
+ TP_printk("nr 0x%lx a0 0x%lx a1 0x%lx a2 0x%lx a3 0x%lx",
+ __entry->nr, __entry->a0, __entry->a1, __entry->a2,
+ __entry->a3)
+);
+
+/*
+ * Tracepoint for PIO.
+ */
+TRACE_EVENT(kvm_pio,
+ TP_PROTO(unsigned int rw, unsigned int port, unsigned int size,
+ unsigned int count),
+ TP_ARGS(rw, port, size, count),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, rw )
+ __field( unsigned int, port )
+ __field( unsigned int, size )
+ __field( unsigned int, count )
+ ),
+
+ TP_fast_assign(
+ __entry->rw = rw;
+ __entry->port = port;
+ __entry->size = size;
+ __entry->count = count;
+ ),
+
+ TP_printk("pio_%s at 0x%x size %d count %d",
+ __entry->rw ? "write" : "read",
+ __entry->port, __entry->size, __entry->count)
+);
+
+/*
+ * Tracepoint for cpuid.
+ */
+TRACE_EVENT(kvm_cpuid,
+ TP_PROTO(unsigned int function, unsigned long rax, unsigned long rbx,
+ unsigned long rcx, unsigned long rdx),
+ TP_ARGS(function, rax, rbx, rcx, rdx),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, function )
+ __field( unsigned long, rax )
+ __field( unsigned long, rbx )
+ __field( unsigned long, rcx )
+ __field( unsigned long, rdx )
+ ),
+
+ TP_fast_assign(
+ __entry->function = function;
+ __entry->rax = rax;
+ __entry->rbx = rbx;
+ __entry->rcx = rcx;
+ __entry->rdx = rdx;
+ ),
+
+ TP_printk("func %x rax %lx rbx %lx rcx %lx rdx %lx",
+ __entry->function, __entry->rax,
+ __entry->rbx, __entry->rcx, __entry->rdx)
+);
+
+#define AREG(x) { APIC_##x, "APIC_" #x }
+
+#define kvm_trace_symbol_apic \
+ AREG(ID), AREG(LVR), AREG(TASKPRI), AREG(ARBPRI), AREG(PROCPRI), \
+ AREG(EOI), AREG(RRR), AREG(LDR), AREG(DFR), AREG(SPIV), AREG(ISR), \
+ AREG(TMR), AREG(IRR), AREG(ESR), AREG(ICR), AREG(ICR2), AREG(LVTT), \
+ AREG(LVTTHMR), AREG(LVTPC), AREG(LVT0), AREG(LVT1), AREG(LVTERR), \
+ AREG(TMICT), AREG(TMCCT), AREG(TDCR), AREG(SELF_IPI), AREG(EFEAT), \
+ AREG(ECTRL)
+/*
+ * Tracepoint for apic access.
+ */
+TRACE_EVENT(kvm_apic,
+ TP_PROTO(unsigned int rw, unsigned int reg, unsigned int val),
+ TP_ARGS(rw, reg, val),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, rw )
+ __field( unsigned int, reg )
+ __field( unsigned int, val )
+ ),
+
+ TP_fast_assign(
+ __entry->rw = rw;
+ __entry->reg = reg;
+ __entry->val = val;
+ ),
+
+ TP_printk("apic_%s %s = 0x%x",
+ __entry->rw ? "write" : "read",
+ __print_symbolic(__entry->reg, kvm_trace_symbol_apic),
+ __entry->val)
+);
+
+#define trace_kvm_apic_read(reg, val) trace_kvm_apic(0, reg, val)
+#define trace_kvm_apic_write(reg, val) trace_kvm_apic(1, reg, val)
+
+/*
+ * Tracepoint for kvm guest exit:
+ */
+TRACE_EVENT(kvm_exit,
+ TP_PROTO(unsigned int exit_reason, unsigned long guest_rip),
+ TP_ARGS(exit_reason, guest_rip),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, exit_reason )
+ __field( unsigned long, guest_rip )
+ ),
+
+ TP_fast_assign(
+ __entry->exit_reason = exit_reason;
+ __entry->guest_rip = guest_rip;
+ ),
+
+ TP_printk("reason %s rip 0x%lx",
+ ftrace_print_symbols_seq(p, __entry->exit_reason,
+ kvm_x86_ops->exit_reasons_str),
+ __entry->guest_rip)
+);
+
+/*
+ * Tracepoint for kvm interrupt injection:
+ */
+TRACE_EVENT(kvm_inj_virq,
+ TP_PROTO(unsigned int irq),
+ TP_ARGS(irq),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, irq )
+ ),
+
+ TP_fast_assign(
+ __entry->irq = irq;
+ ),
+
+ TP_printk("irq %u", __entry->irq)
+);
+
+/*
+ * Tracepoint for page fault.
+ */
+TRACE_EVENT(kvm_page_fault,
+ TP_PROTO(unsigned long fault_address, unsigned int error_code),
+ TP_ARGS(fault_address, error_code),
+
+ TP_STRUCT__entry(
+ __field( unsigned long, fault_address )
+ __field( unsigned int, error_code )
+ ),
+
+ TP_fast_assign(
+ __entry->fault_address = fault_address;
+ __entry->error_code = error_code;
+ ),
+
+ TP_printk("address %lx error_code %x",
+ __entry->fault_address, __entry->error_code)
+);
+
+/*
+ * Tracepoint for guest MSR access.
+ */
+TRACE_EVENT(kvm_msr,
+ TP_PROTO(unsigned int rw, unsigned int ecx, unsigned long data),
+ TP_ARGS(rw, ecx, data),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, rw )
+ __field( unsigned int, ecx )
+ __field( unsigned long, data )
+ ),
+
+ TP_fast_assign(
+ __entry->rw = rw;
+ __entry->ecx = ecx;
+ __entry->data = data;
+ ),
+
+ TP_printk("msr_%s %x = 0x%lx",
+ __entry->rw ? "write" : "read",
+ __entry->ecx, __entry->data)
+);
+
+#define trace_kvm_msr_read(ecx, data) trace_kvm_msr(0, ecx, data)
+#define trace_kvm_msr_write(ecx, data) trace_kvm_msr(1, ecx, data)
+
+/*
+ * Tracepoint for guest CR access.
+ */
+TRACE_EVENT(kvm_cr,
+ TP_PROTO(unsigned int rw, unsigned int cr, unsigned long val),
+ TP_ARGS(rw, cr, val),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, rw )
+ __field( unsigned int, cr )
+ __field( unsigned long, val )
+ ),
+
+ TP_fast_assign(
+ __entry->rw = rw;
+ __entry->cr = cr;
+ __entry->val = val;
+ ),
+
+ TP_printk("cr_%s %x = 0x%lx",
+ __entry->rw ? "write" : "read",
+ __entry->cr, __entry->val)
+);
+
+#define trace_kvm_cr_read(cr, val) trace_kvm_cr(0, cr, val)
+#define trace_kvm_cr_write(cr, val) trace_kvm_cr(1, cr, val)
+
+TRACE_EVENT(kvm_pic_set_irq,
+ TP_PROTO(__u8 chip, __u8 pin, __u8 elcr, __u8 imr, bool coalesced),
+ TP_ARGS(chip, pin, elcr, imr, coalesced),
+
+ TP_STRUCT__entry(
+ __field( __u8, chip )
+ __field( __u8, pin )
+ __field( __u8, elcr )
+ __field( __u8, imr )
+ __field( bool, coalesced )
+ ),
+
+ TP_fast_assign(
+ __entry->chip = chip;
+ __entry->pin = pin;
+ __entry->elcr = elcr;
+ __entry->imr = imr;
+ __entry->coalesced = coalesced;
+ ),
+
+ TP_printk("chip %u pin %u (%s%s)%s",
+ __entry->chip, __entry->pin,
+ (__entry->elcr & (1 << __entry->pin)) ? "level":"edge",
+ (__entry->imr & (1 << __entry->pin)) ? "|masked":"",
+ __entry->coalesced ? " (coalesced)" : "")
+);
+
+#define kvm_apic_dst_shorthand \
+ {0x0, "dst"}, \
+ {0x1, "self"}, \
+ {0x2, "all"}, \
+ {0x3, "all-but-self"}
+
+TRACE_EVENT(kvm_apic_ipi,
+ TP_PROTO(__u32 icr_low, __u32 dest_id),
+ TP_ARGS(icr_low, dest_id),
+
+ TP_STRUCT__entry(
+ __field( __u32, icr_low )
+ __field( __u32, dest_id )
+ ),
+
+ TP_fast_assign(
+ __entry->icr_low = icr_low;
+ __entry->dest_id = dest_id;
+ ),
+
+ TP_printk("dst %x vec %u (%s|%s|%s|%s|%s)",
+ __entry->dest_id, (u8)__entry->icr_low,
+ __print_symbolic((__entry->icr_low >> 8 & 0x7),
+ kvm_deliver_mode),
+ (__entry->icr_low & (1<<11)) ? "logical" : "physical",
+ (__entry->icr_low & (1<<14)) ? "assert" : "de-assert",
+ (__entry->icr_low & (1<<15)) ? "level" : "edge",
+ __print_symbolic((__entry->icr_low >> 18 & 0x3),
+ kvm_apic_dst_shorthand))
+);
+
+TRACE_EVENT(kvm_apic_accept_irq,
+ TP_PROTO(__u32 apicid, __u16 dm, __u8 tm, __u8 vec, bool coalesced),
+ TP_ARGS(apicid, dm, tm, vec, coalesced),
+
+ TP_STRUCT__entry(
+ __field( __u32, apicid )
+ __field( __u16, dm )
+ __field( __u8, tm )
+ __field( __u8, vec )
+ __field( bool, coalesced )
+ ),
+
+ TP_fast_assign(
+ __entry->apicid = apicid;
+ __entry->dm = dm;
+ __entry->tm = tm;
+ __entry->vec = vec;
+ __entry->coalesced = coalesced;
+ ),
+
+ TP_printk("apicid %x vec %u (%s|%s)%s",
+ __entry->apicid, __entry->vec,
+ __print_symbolic((__entry->dm >> 8 & 0x7), kvm_deliver_mode),
+ __entry->tm ? "level" : "edge",
+ __entry->coalesced ? " (coalesced)" : "")
+);
+
+#endif /* _TRACE_KVM_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 29f912927a5..f3812014bd0 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -25,6 +25,7 @@
#include <linux/highmem.h>
#include <linux/sched.h>
#include <linux/moduleparam.h>
+#include <linux/ftrace_event.h>
#include "kvm_cache_regs.h"
#include "x86.h"
@@ -34,6 +35,8 @@
#include <asm/virtext.h>
#include <asm/mce.h>
+#include "trace.h"
+
#define __ex(x) __kvm_handle_fault_on_reboot(x)
MODULE_AUTHOR("Qumranet");
@@ -51,6 +54,10 @@ module_param_named(flexpriority, flexpriority_enabled, bool, S_IRUGO);
static int __read_mostly enable_ept = 1;
module_param_named(ept, enable_ept, bool, S_IRUGO);
+static int __read_mostly enable_unrestricted_guest = 1;
+module_param_named(unrestricted_guest,
+ enable_unrestricted_guest, bool, S_IRUGO);
+
static int __read_mostly emulate_invalid_guest_state = 0;
module_param(emulate_invalid_guest_state, bool, S_IRUGO);
@@ -84,6 +91,14 @@ struct vcpu_vmx {
int guest_efer_loaded;
} host_state;
struct {
+ int vm86_active;
+ u8 save_iopl;
+ struct kvm_save_segment {
+ u16 selector;
+ unsigned long base;
+ u32 limit;
+ u32 ar;
+ } tr, es, ds, fs, gs;
struct {
bool pending;
u8 vector;
@@ -161,6 +176,8 @@ static struct kvm_vmx_segment_field {
VMX_SEGMENT_FIELD(LDTR),
};
+static void ept_save_pdptrs(struct kvm_vcpu *vcpu);
+
/*
* Keep MSR_K6_STAR at the end, as setup_msrs() will try to optimize it
* away by decrementing the array size.
@@ -256,6 +273,26 @@ static inline bool cpu_has_vmx_flexpriority(void)
cpu_has_vmx_virtualize_apic_accesses();
}
+static inline bool cpu_has_vmx_ept_execute_only(void)
+{
+ return !!(vmx_capability.ept & VMX_EPT_EXECUTE_ONLY_BIT);
+}
+
+static inline bool cpu_has_vmx_eptp_uncacheable(void)
+{
+ return !!(vmx_capability.ept & VMX_EPTP_UC_BIT);
+}
+
+static inline bool cpu_has_vmx_eptp_writeback(void)
+{
+ return !!(vmx_capability.ept & VMX_EPTP_WB_BIT);
+}
+
+static inline bool cpu_has_vmx_ept_2m_page(void)
+{
+ return !!(vmx_capability.ept & VMX_EPT_2MB_PAGE_BIT);
+}
+
static inline int cpu_has_vmx_invept_individual_addr(void)
{
return !!(vmx_capability.ept & VMX_EPT_EXTENT_INDIVIDUAL_BIT);
@@ -277,6 +314,12 @@ static inline int cpu_has_vmx_ept(void)
SECONDARY_EXEC_ENABLE_EPT;
}
+static inline int cpu_has_vmx_unrestricted_guest(void)
+{
+ return vmcs_config.cpu_based_2nd_exec_ctrl &
+ SECONDARY_EXEC_UNRESTRICTED_GUEST;
+}
+
static inline int vm_need_virtualize_apic_accesses(struct kvm *kvm)
{
return flexpriority_enabled &&
@@ -497,14 +540,16 @@ static void update_exception_bitmap(struct kvm_vcpu *vcpu)
eb = (1u << PF_VECTOR) | (1u << UD_VECTOR) | (1u << MC_VECTOR);
if (!vcpu->fpu_active)
eb |= 1u << NM_VECTOR;
+ /*
+ * Unconditionally intercept #DB so we can maintain dr6 without
+ * reading it every exit.
+ */
+ eb |= 1u << DB_VECTOR;
if (vcpu->guest_debug & KVM_GUESTDBG_ENABLE) {
- if (vcpu->guest_debug &
- (KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_USE_HW_BP))
- eb |= 1u << DB_VECTOR;
if (vcpu->guest_debug & KVM_GUESTDBG_USE_SW_BP)
eb |= 1u << BP_VECTOR;
}
- if (vcpu->arch.rmode.vm86_active)
+ if (to_vmx(vcpu)->rmode.vm86_active)
eb = ~0;
if (enable_ept)
eb &= ~(1u << PF_VECTOR); /* bypass_guest_pf = 0 */
@@ -528,12 +573,15 @@ static void reload_tss(void)
static void load_transition_efer(struct vcpu_vmx *vmx)
{
int efer_offset = vmx->msr_offset_efer;
- u64 host_efer = vmx->host_msrs[efer_offset].data;
- u64 guest_efer = vmx->guest_msrs[efer_offset].data;
+ u64 host_efer;
+ u64 guest_efer;
u64 ignore_bits;
if (efer_offset < 0)
return;
+ host_efer = vmx->host_msrs[efer_offset].data;
+ guest_efer = vmx->guest_msrs[efer_offset].data;
+
/*
* NX is emulated; LMA and LME handled by hardware; SCE meaninless
* outside long mode
@@ -735,12 +783,17 @@ static void vmx_fpu_deactivate(struct kvm_vcpu *vcpu)
static unsigned long vmx_get_rflags(struct kvm_vcpu *vcpu)
{
- return vmcs_readl(GUEST_RFLAGS);
+ unsigned long rflags;
+
+ rflags = vmcs_readl(GUEST_RFLAGS);
+ if (to_vmx(vcpu)->rmode.vm86_active)
+ rflags &= ~(unsigned long)(X86_EFLAGS_IOPL | X86_EFLAGS_VM);
+ return rflags;
}
static void vmx_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags)
{
- if (vcpu->arch.rmode.vm86_active)
+ if (to_vmx(vcpu)->rmode.vm86_active)
rflags |= X86_EFLAGS_IOPL | X86_EFLAGS_VM;
vmcs_writel(GUEST_RFLAGS, rflags);
}
@@ -797,12 +850,13 @@ static void vmx_queue_exception(struct kvm_vcpu *vcpu, unsigned nr,
intr_info |= INTR_INFO_DELIVER_CODE_MASK;
}
- if (vcpu->arch.rmode.vm86_active) {
+ if (vmx->rmode.vm86_active) {
vmx->rmode.irq.pending = true;
vmx->rmode.irq.vector = nr;
vmx->rmode.irq.rip = kvm_rip_read(vcpu);
- if (nr == BP_VECTOR || nr == OF_VECTOR)
- vmx->rmode.irq.rip++;
+ if (kvm_exception_is_soft(nr))
+ vmx->rmode.irq.rip +=
+ vmx->vcpu.arch.event_exit_inst_len;
intr_info |= INTR_TYPE_SOFT_INTR;
vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info);
vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, 1);
@@ -940,7 +994,7 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata)
case MSR_EFER:
return kvm_get_msr_common(vcpu, msr_index, pdata);
#endif
- case MSR_IA32_TIME_STAMP_COUNTER:
+ case MSR_IA32_TSC:
data = guest_read_tsc();
break;
case MSR_IA32_SYSENTER_CS:
@@ -953,9 +1007,9 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata)
data = vmcs_readl(GUEST_SYSENTER_ESP);
break;
default:
- vmx_load_host_state(to_vmx(vcpu));
msr = find_msr_entry(to_vmx(vcpu), msr_index);
if (msr) {
+ vmx_load_host_state(to_vmx(vcpu));
data = msr->data;
break;
}
@@ -1000,22 +1054,10 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data)
case MSR_IA32_SYSENTER_ESP:
vmcs_writel(GUEST_SYSENTER_ESP, data);
break;
- case MSR_IA32_TIME_STAMP_COUNTER:
+ case MSR_IA32_TSC:
rdtscll(host_tsc);
guest_write_tsc(data, host_tsc);
break;
- case MSR_P6_PERFCTR0:
- case MSR_P6_PERFCTR1:
- case MSR_P6_EVNTSEL0:
- case MSR_P6_EVNTSEL1:
- /*
- * Just discard all writes to the performance counters; this
- * should keep both older linux and windows 64-bit guests
- * happy
- */
- pr_unimpl(vcpu, "unimplemented perfctr wrmsr: 0x%x data 0x%llx\n", msr_index, data);
-
- break;
case MSR_IA32_CR_PAT:
if (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PAT) {
vmcs_write64(GUEST_IA32_PAT, data);
@@ -1024,9 +1066,9 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data)
}
/* Otherwise falls through to kvm_set_msr_common */
default:
- vmx_load_host_state(vmx);
msr = find_msr_entry(vmx, msr_index);
if (msr) {
+ vmx_load_host_state(vmx);
msr->data = data;
break;
}
@@ -1046,6 +1088,10 @@ static void vmx_cache_reg(struct kvm_vcpu *vcpu, enum kvm_reg reg)
case VCPU_REGS_RIP:
vcpu->arch.regs[VCPU_REGS_RIP] = vmcs_readl(GUEST_RIP);
break;
+ case VCPU_EXREG_PDPTR:
+ if (enable_ept)
+ ept_save_pdptrs(vcpu);
+ break;
default:
break;
}
@@ -1203,7 +1249,8 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
opt2 = SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES |
SECONDARY_EXEC_WBINVD_EXITING |
SECONDARY_EXEC_ENABLE_VPID |
- SECONDARY_EXEC_ENABLE_EPT;
+ SECONDARY_EXEC_ENABLE_EPT |
+ SECONDARY_EXEC_UNRESTRICTED_GUEST;
if (adjust_vmx_controls(min2, opt2,
MSR_IA32_VMX_PROCBASED_CTLS2,
&_cpu_based_2nd_exec_control) < 0)
@@ -1217,12 +1264,9 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
if (_cpu_based_2nd_exec_control & SECONDARY_EXEC_ENABLE_EPT) {
/* CR3 accesses and invlpg don't need to cause VM Exits when EPT
enabled */
- min &= ~(CPU_BASED_CR3_LOAD_EXITING |
- CPU_BASED_CR3_STORE_EXITING |
- CPU_BASED_INVLPG_EXITING);
- if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_PROCBASED_CTLS,
- &_cpu_based_exec_control) < 0)
- return -EIO;
+ _cpu_based_exec_control &= ~(CPU_BASED_CR3_LOAD_EXITING |
+ CPU_BASED_CR3_STORE_EXITING |
+ CPU_BASED_INVLPG_EXITING);
rdmsr(MSR_IA32_VMX_EPT_VPID_CAP,
vmx_capability.ept, vmx_capability.vpid);
}
@@ -1333,8 +1377,13 @@ static __init int hardware_setup(void)
if (!cpu_has_vmx_vpid())
enable_vpid = 0;
- if (!cpu_has_vmx_ept())
+ if (!cpu_has_vmx_ept()) {
enable_ept = 0;
+ enable_unrestricted_guest = 0;
+ }
+
+ if (!cpu_has_vmx_unrestricted_guest())
+ enable_unrestricted_guest = 0;
if (!cpu_has_vmx_flexpriority())
flexpriority_enabled = 0;
@@ -1342,6 +1391,9 @@ static __init int hardware_setup(void)
if (!cpu_has_vmx_tpr_shadow())
kvm_x86_ops->update_cr8_intercept = NULL;
+ if (enable_ept && !cpu_has_vmx_ept_2m_page())
+ kvm_disable_largepages();
+
return alloc_kvm_area();
}
@@ -1372,15 +1424,15 @@ static void enter_pmode(struct kvm_vcpu *vcpu)
struct vcpu_vmx *vmx = to_vmx(vcpu);
vmx->emulation_required = 1;
- vcpu->arch.rmode.vm86_active = 0;
+ vmx->rmode.vm86_active = 0;
- vmcs_writel(GUEST_TR_BASE, vcpu->arch.rmode.tr.base);
- vmcs_write32(GUEST_TR_LIMIT, vcpu->arch.rmode.tr.limit);
- vmcs_write32(GUEST_TR_AR_BYTES, vcpu->arch.rmode.tr.ar);
+ vmcs_writel(GUEST_TR_BASE, vmx->rmode.tr.base);
+ vmcs_write32(GUEST_TR_LIMIT, vmx->rmode.tr.limit);
+ vmcs_write32(GUEST_TR_AR_BYTES, vmx->rmode.tr.ar);
flags = vmcs_readl(GUEST_RFLAGS);
flags &= ~(X86_EFLAGS_IOPL | X86_EFLAGS_VM);
- flags |= (vcpu->arch.rmode.save_iopl << IOPL_SHIFT);
+ flags |= (vmx->rmode.save_iopl << IOPL_SHIFT);
vmcs_writel(GUEST_RFLAGS, flags);
vmcs_writel(GUEST_CR4, (vmcs_readl(GUEST_CR4) & ~X86_CR4_VME) |
@@ -1391,10 +1443,10 @@ static void enter_pmode(struct kvm_vcpu *vcpu)
if (emulate_invalid_guest_state)
return;
- fix_pmode_dataseg(VCPU_SREG_ES, &vcpu->arch.rmode.es);
- fix_pmode_dataseg(VCPU_SREG_DS, &vcpu->arch.rmode.ds);
- fix_pmode_dataseg(VCPU_SREG_GS, &vcpu->arch.rmode.gs);
- fix_pmode_dataseg(VCPU_SREG_FS, &vcpu->arch.rmode.fs);
+ fix_pmode_dataseg(VCPU_SREG_ES, &vmx->rmode.es);
+ fix_pmode_dataseg(VCPU_SREG_DS, &vmx->rmode.ds);
+ fix_pmode_dataseg(VCPU_SREG_GS, &vmx->rmode.gs);
+ fix_pmode_dataseg(VCPU_SREG_FS, &vmx->rmode.fs);
vmcs_write16(GUEST_SS_SELECTOR, 0);
vmcs_write32(GUEST_SS_AR_BYTES, 0x93);
@@ -1433,20 +1485,23 @@ static void enter_rmode(struct kvm_vcpu *vcpu)
unsigned long flags;
struct vcpu_vmx *vmx = to_vmx(vcpu);
+ if (enable_unrestricted_guest)
+ return;
+
vmx->emulation_required = 1;
- vcpu->arch.rmode.vm86_active = 1;
+ vmx->rmode.vm86_active = 1;
- vcpu->arch.rmode.tr.base = vmcs_readl(GUEST_TR_BASE);
+ vmx->rmode.tr.base = vmcs_readl(GUEST_TR_BASE);
vmcs_writel(GUEST_TR_BASE, rmode_tss_base(vcpu->kvm));
- vcpu->arch.rmode.tr.limit = vmcs_read32(GUEST_TR_LIMIT);
+ vmx->rmode.tr.limit = vmcs_read32(GUEST_TR_LIMIT);
vmcs_write32(GUEST_TR_LIMIT, RMODE_TSS_SIZE - 1);
- vcpu->arch.rmode.tr.ar = vmcs_read32(GUEST_TR_AR_BYTES);
+ vmx->rmode.tr.ar = vmcs_read32(GUEST_TR_AR_BYTES);
vmcs_write32(GUEST_TR_AR_BYTES, 0x008b);
flags = vmcs_readl(GUEST_RFLAGS);
- vcpu->arch.rmode.save_iopl
+ vmx->rmode.save_iopl
= (flags & X86_EFLAGS_IOPL) >> IOPL_SHIFT;
flags |= X86_EFLAGS_IOPL | X86_EFLAGS_VM;
@@ -1468,10 +1523,10 @@ static void enter_rmode(struct kvm_vcpu *vcpu)
vmcs_writel(GUEST_CS_BASE, 0xf0000);
vmcs_write16(GUEST_CS_SELECTOR, vmcs_readl(GUEST_CS_BASE) >> 4);
- fix_rmode_seg(VCPU_SREG_ES, &vcpu->arch.rmode.es);
- fix_rmode_seg(VCPU_SREG_DS, &vcpu->arch.rmode.ds);
- fix_rmode_seg(VCPU_SREG_GS, &vcpu->arch.rmode.gs);
- fix_rmode_seg(VCPU_SREG_FS, &vcpu->arch.rmode.fs);
+ fix_rmode_seg(VCPU_SREG_ES, &vmx->rmode.es);
+ fix_rmode_seg(VCPU_SREG_DS, &vmx->rmode.ds);
+ fix_rmode_seg(VCPU_SREG_GS, &vmx->rmode.gs);
+ fix_rmode_seg(VCPU_SREG_FS, &vmx->rmode.fs);
continue_rmode:
kvm_mmu_reset_context(vcpu);
@@ -1545,11 +1600,11 @@ static void vmx_decache_cr4_guest_bits(struct kvm_vcpu *vcpu)
static void ept_load_pdptrs(struct kvm_vcpu *vcpu)
{
+ if (!test_bit(VCPU_EXREG_PDPTR,
+ (unsigned long *)&vcpu->arch.regs_dirty))
+ return;
+
if (is_paging(vcpu) && is_pae(vcpu) && !is_long_mode(vcpu)) {
- if (!load_pdptrs(vcpu, vcpu->arch.cr3)) {
- printk(KERN_ERR "EPT: Fail to load pdptrs!\n");
- return;
- }
vmcs_write64(GUEST_PDPTR0, vcpu->arch.pdptrs[0]);
vmcs_write64(GUEST_PDPTR1, vcpu->arch.pdptrs[1]);
vmcs_write64(GUEST_PDPTR2, vcpu->arch.pdptrs[2]);
@@ -1557,6 +1612,21 @@ static void ept_load_pdptrs(struct kvm_vcpu *vcpu)
}
}
+static void ept_save_pdptrs(struct kvm_vcpu *vcpu)
+{
+ if (is_paging(vcpu) && is_pae(vcpu) && !is_long_mode(vcpu)) {
+ vcpu->arch.pdptrs[0] = vmcs_read64(GUEST_PDPTR0);
+ vcpu->arch.pdptrs[1] = vmcs_read64(GUEST_PDPTR1);
+ vcpu->arch.pdptrs[2] = vmcs_read64(GUEST_PDPTR2);
+ vcpu->arch.pdptrs[3] = vmcs_read64(GUEST_PDPTR3);
+ }
+
+ __set_bit(VCPU_EXREG_PDPTR,
+ (unsigned long *)&vcpu->arch.regs_avail);
+ __set_bit(VCPU_EXREG_PDPTR,
+ (unsigned long *)&vcpu->arch.regs_dirty);
+}
+
static void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4);
static void ept_update_paging_mode_cr0(unsigned long *hw_cr0,
@@ -1571,8 +1641,6 @@ static void ept_update_paging_mode_cr0(unsigned long *hw_cr0,
CPU_BASED_CR3_STORE_EXITING));
vcpu->arch.cr0 = cr0;
vmx_set_cr4(vcpu, vcpu->arch.cr4);
- *hw_cr0 |= X86_CR0_PE | X86_CR0_PG;
- *hw_cr0 &= ~X86_CR0_WP;
} else if (!is_paging(vcpu)) {
/* From nonpaging to paging */
vmcs_write32(CPU_BASED_VM_EXEC_CONTROL,
@@ -1581,9 +1649,10 @@ static void ept_update_paging_mode_cr0(unsigned long *hw_cr0,
CPU_BASED_CR3_STORE_EXITING));
vcpu->arch.cr0 = cr0;
vmx_set_cr4(vcpu, vcpu->arch.cr4);
- if (!(vcpu->arch.cr0 & X86_CR0_WP))
- *hw_cr0 &= ~X86_CR0_WP;
}
+
+ if (!(cr0 & X86_CR0_WP))
+ *hw_cr0 &= ~X86_CR0_WP;
}
static void ept_update_paging_mode_cr4(unsigned long *hw_cr4,
@@ -1598,15 +1667,21 @@ static void ept_update_paging_mode_cr4(unsigned long *hw_cr4,
static void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0)
{
- unsigned long hw_cr0 = (cr0 & ~KVM_GUEST_CR0_MASK) |
- KVM_VM_CR0_ALWAYS_ON;
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
+ unsigned long hw_cr0;
+
+ if (enable_unrestricted_guest)
+ hw_cr0 = (cr0 & ~KVM_GUEST_CR0_MASK_UNRESTRICTED_GUEST)
+ | KVM_VM_CR0_ALWAYS_ON_UNRESTRICTED_GUEST;
+ else
+ hw_cr0 = (cr0 & ~KVM_GUEST_CR0_MASK) | KVM_VM_CR0_ALWAYS_ON;
vmx_fpu_deactivate(vcpu);
- if (vcpu->arch.rmode.vm86_active && (cr0 & X86_CR0_PE))
+ if (vmx->rmode.vm86_active && (cr0 & X86_CR0_PE))
enter_pmode(vcpu);
- if (!vcpu->arch.rmode.vm86_active && !(cr0 & X86_CR0_PE))
+ if (!vmx->rmode.vm86_active && !(cr0 & X86_CR0_PE))
enter_rmode(vcpu);
#ifdef CONFIG_X86_64
@@ -1650,10 +1725,8 @@ static void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
if (enable_ept) {
eptp = construct_eptp(cr3);
vmcs_write64(EPT_POINTER, eptp);
- ept_sync_context(eptp);
- ept_load_pdptrs(vcpu);
guest_cr3 = is_paging(vcpu) ? vcpu->arch.cr3 :
- VMX_EPT_IDENTITY_PAGETABLE_ADDR;
+ vcpu->kvm->arch.ept_identity_map_addr;
}
vmx_flush_tlb(vcpu);
@@ -1664,7 +1737,7 @@ static void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
static void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
{
- unsigned long hw_cr4 = cr4 | (vcpu->arch.rmode.vm86_active ?
+ unsigned long hw_cr4 = cr4 | (to_vmx(vcpu)->rmode.vm86_active ?
KVM_RMODE_VM_CR4_ALWAYS_ON : KVM_PMODE_VM_CR4_ALWAYS_ON);
vcpu->arch.cr4 = cr4;
@@ -1707,16 +1780,13 @@ static void vmx_get_segment(struct kvm_vcpu *vcpu,
static int vmx_get_cpl(struct kvm_vcpu *vcpu)
{
- struct kvm_segment kvm_seg;
-
if (!(vcpu->arch.cr0 & X86_CR0_PE)) /* if real mode */
return 0;
if (vmx_get_rflags(vcpu) & X86_EFLAGS_VM) /* if virtual 8086 */
return 3;
- vmx_get_segment(vcpu, &kvm_seg, VCPU_SREG_CS);
- return kvm_seg.selector & 3;
+ return vmcs_read16(GUEST_CS_SELECTOR) & 3;
}
static u32 vmx_segment_access_rights(struct kvm_segment *var)
@@ -1744,20 +1814,21 @@ static u32 vmx_segment_access_rights(struct kvm_segment *var)
static void vmx_set_segment(struct kvm_vcpu *vcpu,
struct kvm_segment *var, int seg)
{
+ struct vcpu_vmx *vmx = to_vmx(vcpu);
struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
u32 ar;
- if (vcpu->arch.rmode.vm86_active && seg == VCPU_SREG_TR) {
- vcpu->arch.rmode.tr.selector = var->selector;
- vcpu->arch.rmode.tr.base = var->base;
- vcpu->arch.rmode.tr.limit = var->limit;
- vcpu->arch.rmode.tr.ar = vmx_segment_access_rights(var);
+ if (vmx->rmode.vm86_active && seg == VCPU_SREG_TR) {
+ vmx->rmode.tr.selector = var->selector;
+ vmx->rmode.tr.base = var->base;
+ vmx->rmode.tr.limit = var->limit;
+ vmx->rmode.tr.ar = vmx_segment_access_rights(var);
return;
}
vmcs_writel(sf->base, var->base);
vmcs_write32(sf->limit, var->limit);
vmcs_write16(sf->selector, var->selector);
- if (vcpu->arch.rmode.vm86_active && var->s) {
+ if (vmx->rmode.vm86_active && var->s) {
/*
* Hack real-mode segments into vm86 compatibility.
*/
@@ -1766,6 +1837,21 @@ static void vmx_set_segment(struct kvm_vcpu *vcpu,
ar = 0xf3;
} else
ar = vmx_segment_access_rights(var);
+
+ /*
+ * Fix the "Accessed" bit in AR field of segment registers for older
+ * qemu binaries.
+ * IA32 arch specifies that at the time of processor reset the
+ * "Accessed" bit in the AR field of segment registers is 1. And qemu
+ * is setting it to 0 in the usedland code. This causes invalid guest
+ * state vmexit when "unrestricted guest" mode is turned on.
+ * Fix for this setup issue in cpu_reset is being pushed in the qemu
+ * tree. Newer qemu binaries with that qemu fix would not need this
+ * kvm hack.
+ */
+ if (enable_unrestricted_guest && (seg != VCPU_SREG_LDTR))
+ ar |= 0x1; /* Accessed */
+
vmcs_write32(sf->ar_bytes, ar);
}
@@ -2040,7 +2126,7 @@ static int init_rmode_identity_map(struct kvm *kvm)
if (likely(kvm->arch.ept_identity_pagetable_done))
return 1;
ret = 0;
- identity_map_pfn = VMX_EPT_IDENTITY_PAGETABLE_ADDR >> PAGE_SHIFT;
+ identity_map_pfn = kvm->arch.ept_identity_map_addr >> PAGE_SHIFT;
r = kvm_clear_guest_page(kvm, identity_map_pfn, 0, PAGE_SIZE);
if (r < 0)
goto out;
@@ -2062,11 +2148,19 @@ out:
static void seg_setup(int seg)
{
struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg];
+ unsigned int ar;
vmcs_write16(sf->selector, 0);
vmcs_writel(sf->base, 0);
vmcs_write32(sf->limit, 0xffff);
- vmcs_write32(sf->ar_bytes, 0xf3);
+ if (enable_unrestricted_guest) {
+ ar = 0x93;
+ if (seg == VCPU_SREG_CS)
+ ar |= 0x08; /* code segment */
+ } else
+ ar = 0xf3;
+
+ vmcs_write32(sf->ar_bytes, ar);
}
static int alloc_apic_access_page(struct kvm *kvm)
@@ -2101,14 +2195,15 @@ static int alloc_identity_pagetable(struct kvm *kvm)
goto out;
kvm_userspace_mem.slot = IDENTITY_PAGETABLE_PRIVATE_MEMSLOT;
kvm_userspace_mem.flags = 0;
- kvm_userspace_mem.guest_phys_addr = VMX_EPT_IDENTITY_PAGETABLE_ADDR;
+ kvm_userspace_mem.guest_phys_addr =
+ kvm->arch.ept_identity_map_addr;
kvm_userspace_mem.memory_size = PAGE_SIZE;
r = __kvm_set_memory_region(kvm, &kvm_userspace_mem, 0);
if (r)
goto out;
kvm->arch.ept_identity_pagetable = gfn_to_page(kvm,
- VMX_EPT_IDENTITY_PAGETABLE_ADDR >> PAGE_SHIFT);
+ kvm->arch.ept_identity_map_addr >> PAGE_SHIFT);
out:
up_write(&kvm->slots_lock);
return r;
@@ -2209,6 +2304,8 @@ static int vmx_vcpu_setup(struct vcpu_vmx *vmx)
exec_control &= ~SECONDARY_EXEC_ENABLE_VPID;
if (!enable_ept)
exec_control &= ~SECONDARY_EXEC_ENABLE_EPT;
+ if (!enable_unrestricted_guest)
+ exec_control &= ~SECONDARY_EXEC_UNRESTRICTED_GUEST;
vmcs_write32(SECONDARY_VM_EXEC_CONTROL, exec_control);
}
@@ -2326,14 +2423,14 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu)
goto out;
}
- vmx->vcpu.arch.rmode.vm86_active = 0;
+ vmx->rmode.vm86_active = 0;
vmx->soft_vnmi_blocked = 0;
vmx->vcpu.arch.regs[VCPU_REGS_RDX] = get_rdx_init_val();
kvm_set_cr8(&vmx->vcpu, 0);
msr = 0xfee00000 | MSR_IA32_APICBASE_ENABLE;
- if (vmx->vcpu.vcpu_id == 0)
+ if (kvm_vcpu_is_bsp(&vmx->vcpu))
msr |= MSR_IA32_APICBASE_BSP;
kvm_set_apic_base(&vmx->vcpu, msr);
@@ -2344,7 +2441,7 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu)
* GUEST_CS_BASE should really be 0xffff0000, but VT vm86 mode
* insists on having GUEST_CS_BASE == GUEST_CS_SELECTOR << 4. Sigh.
*/
- if (vmx->vcpu.vcpu_id == 0) {
+ if (kvm_vcpu_is_bsp(&vmx->vcpu)) {
vmcs_write16(GUEST_CS_SELECTOR, 0xf000);
vmcs_writel(GUEST_CS_BASE, 0x000f0000);
} else {
@@ -2373,7 +2470,7 @@ static int vmx_vcpu_reset(struct kvm_vcpu *vcpu)
vmcs_writel(GUEST_SYSENTER_EIP, 0);
vmcs_writel(GUEST_RFLAGS, 0x02);
- if (vmx->vcpu.vcpu_id == 0)
+ if (kvm_vcpu_is_bsp(&vmx->vcpu))
kvm_rip_write(vcpu, 0xfff0);
else
kvm_rip_write(vcpu, 0);
@@ -2461,13 +2558,16 @@ static void vmx_inject_irq(struct kvm_vcpu *vcpu)
uint32_t intr;
int irq = vcpu->arch.interrupt.nr;
- KVMTRACE_1D(INJ_VIRQ, vcpu, (u32)irq, handler);
+ trace_kvm_inj_virq(irq);
++vcpu->stat.irq_injections;
- if (vcpu->arch.rmode.vm86_active) {
+ if (vmx->rmode.vm86_active) {
vmx->rmode.irq.pending = true;
vmx->rmode.irq.vector = irq;
vmx->rmode.irq.rip = kvm_rip_read(vcpu);
+ if (vcpu->arch.interrupt.soft)
+ vmx->rmode.irq.rip +=
+ vmx->vcpu.arch.event_exit_inst_len;
vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
irq | INTR_TYPE_SOFT_INTR | INTR_INFO_VALID_MASK);
vmcs_write32(VM_ENTRY_INSTRUCTION_LEN, 1);
@@ -2502,7 +2602,7 @@ static void vmx_inject_nmi(struct kvm_vcpu *vcpu)
}
++vcpu->stat.nmi_injections;
- if (vcpu->arch.rmode.vm86_active) {
+ if (vmx->rmode.vm86_active) {
vmx->rmode.irq.pending = true;
vmx->rmode.irq.vector = NMI_VECTOR;
vmx->rmode.irq.rip = kvm_rip_read(vcpu);
@@ -2659,14 +2759,14 @@ static int handle_exception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
if (enable_ept)
BUG();
cr2 = vmcs_readl(EXIT_QUALIFICATION);
- KVMTRACE_3D(PAGE_FAULT, vcpu, error_code, (u32)cr2,
- (u32)((u64)cr2 >> 32), handler);
+ trace_kvm_page_fault(cr2, error_code);
+
if (kvm_event_needs_reinjection(vcpu))
kvm_mmu_unprotect_page_virt(vcpu, cr2);
return kvm_mmu_page_fault(vcpu, cr2, error_code);
}
- if (vcpu->arch.rmode.vm86_active &&
+ if (vmx->rmode.vm86_active &&
handle_rmode_exception(vcpu, intr_info & INTR_INFO_VECTOR_MASK,
error_code)) {
if (vcpu->arch.halt_request) {
@@ -2707,7 +2807,6 @@ static int handle_external_interrupt(struct kvm_vcpu *vcpu,
struct kvm_run *kvm_run)
{
++vcpu->stat.irq_exits;
- KVMTRACE_1D(INTR, vcpu, vmcs_read32(VM_EXIT_INTR_INFO), handler);
return 1;
}
@@ -2755,7 +2854,7 @@ vmx_patch_hypercall(struct kvm_vcpu *vcpu, unsigned char *hypercall)
static int handle_cr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
- unsigned long exit_qualification;
+ unsigned long exit_qualification, val;
int cr;
int reg;
@@ -2764,21 +2863,19 @@ static int handle_cr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
reg = (exit_qualification >> 8) & 15;
switch ((exit_qualification >> 4) & 3) {
case 0: /* mov to cr */
- KVMTRACE_3D(CR_WRITE, vcpu, (u32)cr,
- (u32)kvm_register_read(vcpu, reg),
- (u32)((u64)kvm_register_read(vcpu, reg) >> 32),
- handler);
+ val = kvm_register_read(vcpu, reg);
+ trace_kvm_cr_write(cr, val);
switch (cr) {
case 0:
- kvm_set_cr0(vcpu, kvm_register_read(vcpu, reg));
+ kvm_set_cr0(vcpu, val);
skip_emulated_instruction(vcpu);
return 1;
case 3:
- kvm_set_cr3(vcpu, kvm_register_read(vcpu, reg));
+ kvm_set_cr3(vcpu, val);
skip_emulated_instruction(vcpu);
return 1;
case 4:
- kvm_set_cr4(vcpu, kvm_register_read(vcpu, reg));
+ kvm_set_cr4(vcpu, val);
skip_emulated_instruction(vcpu);
return 1;
case 8: {
@@ -2800,23 +2897,19 @@ static int handle_cr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
vcpu->arch.cr0 &= ~X86_CR0_TS;
vmcs_writel(CR0_READ_SHADOW, vcpu->arch.cr0);
vmx_fpu_activate(vcpu);
- KVMTRACE_0D(CLTS, vcpu, handler);
skip_emulated_instruction(vcpu);
return 1;
case 1: /*mov from cr*/
switch (cr) {
case 3:
kvm_register_write(vcpu, reg, vcpu->arch.cr3);
- KVMTRACE_3D(CR_READ, vcpu, (u32)cr,
- (u32)kvm_register_read(vcpu, reg),
- (u32)((u64)kvm_register_read(vcpu, reg) >> 32),
- handler);
+ trace_kvm_cr_read(cr, vcpu->arch.cr3);
skip_emulated_instruction(vcpu);
return 1;
case 8:
- kvm_register_write(vcpu, reg, kvm_get_cr8(vcpu));
- KVMTRACE_2D(CR_READ, vcpu, (u32)cr,
- (u32)kvm_register_read(vcpu, reg), handler);
+ val = kvm_get_cr8(vcpu);
+ kvm_register_write(vcpu, reg, val);
+ trace_kvm_cr_read(cr, val);
skip_emulated_instruction(vcpu);
return 1;
}
@@ -2841,6 +2934,8 @@ static int handle_dr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
unsigned long val;
int dr, reg;
+ if (!kvm_require_cpl(vcpu, 0))
+ return 1;
dr = vmcs_readl(GUEST_DR7);
if (dr & DR7_GD) {
/*
@@ -2884,7 +2979,6 @@ static int handle_dr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
val = 0;
}
kvm_register_write(vcpu, reg, val);
- KVMTRACE_2D(DR_READ, vcpu, (u32)dr, (u32)val, handler);
} else {
val = vcpu->arch.regs[reg];
switch (dr) {
@@ -2917,7 +3011,6 @@ static int handle_dr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
}
break;
}
- KVMTRACE_2D(DR_WRITE, vcpu, (u32)dr, (u32)val, handler);
}
skip_emulated_instruction(vcpu);
return 1;
@@ -2939,8 +3032,7 @@ static int handle_rdmsr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
return 1;
}
- KVMTRACE_3D(MSR_READ, vcpu, ecx, (u32)data, (u32)(data >> 32),
- handler);
+ trace_kvm_msr_read(ecx, data);
/* FIXME: handling of bits 32:63 of rax, rdx */
vcpu->arch.regs[VCPU_REGS_RAX] = data & -1u;
@@ -2955,8 +3047,7 @@ static int handle_wrmsr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
u64 data = (vcpu->arch.regs[VCPU_REGS_RAX] & -1u)
| ((u64)(vcpu->arch.regs[VCPU_REGS_RDX] & -1u) << 32);
- KVMTRACE_3D(MSR_WRITE, vcpu, ecx, (u32)data, (u32)(data >> 32),
- handler);
+ trace_kvm_msr_write(ecx, data);
if (vmx_set_msr(vcpu, ecx, data) != 0) {
kvm_inject_gp(vcpu, 0);
@@ -2983,7 +3074,6 @@ static int handle_interrupt_window(struct kvm_vcpu *vcpu,
cpu_based_vm_exec_control &= ~CPU_BASED_VIRTUAL_INTR_PENDING;
vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);
- KVMTRACE_0D(PEND_INTR, vcpu, handler);
++vcpu->stat.irq_window_exits;
/*
@@ -3049,7 +3139,7 @@ static int handle_apic_access(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
printk(KERN_ERR
"Fail to handle apic access vmexit! Offset is 0x%lx\n",
offset);
- return -ENOTSUPP;
+ return -ENOEXEC;
}
return 1;
}
@@ -3118,7 +3208,7 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
if (exit_qualification & (1 << 6)) {
printk(KERN_ERR "EPT: GPA exceeds GAW!\n");
- return -ENOTSUPP;
+ return -EINVAL;
}
gla_validity = (exit_qualification >> 7) & 0x3;
@@ -3130,14 +3220,98 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
printk(KERN_ERR "EPT: Exit qualification is 0x%lx\n",
(long unsigned int)exit_qualification);
kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
- kvm_run->hw.hardware_exit_reason = 0;
- return -ENOTSUPP;
+ kvm_run->hw.hardware_exit_reason = EXIT_REASON_EPT_VIOLATION;
+ return 0;
}
gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS);
+ trace_kvm_page_fault(gpa, exit_qualification);
return kvm_mmu_page_fault(vcpu, gpa & PAGE_MASK, 0);
}
+static u64 ept_rsvd_mask(u64 spte, int level)
+{
+ int i;
+ u64 mask = 0;
+
+ for (i = 51; i > boot_cpu_data.x86_phys_bits; i--)
+ mask |= (1ULL << i);
+
+ if (level > 2)
+ /* bits 7:3 reserved */
+ mask |= 0xf8;
+ else if (level == 2) {
+ if (spte & (1ULL << 7))
+ /* 2MB ref, bits 20:12 reserved */
+ mask |= 0x1ff000;
+ else
+ /* bits 6:3 reserved */
+ mask |= 0x78;
+ }
+
+ return mask;
+}
+
+static void ept_misconfig_inspect_spte(struct kvm_vcpu *vcpu, u64 spte,
+ int level)
+{
+ printk(KERN_ERR "%s: spte 0x%llx level %d\n", __func__, spte, level);
+
+ /* 010b (write-only) */
+ WARN_ON((spte & 0x7) == 0x2);
+
+ /* 110b (write/execute) */
+ WARN_ON((spte & 0x7) == 0x6);
+
+ /* 100b (execute-only) and value not supported by logical processor */
+ if (!cpu_has_vmx_ept_execute_only())
+ WARN_ON((spte & 0x7) == 0x4);
+
+ /* not 000b */
+ if ((spte & 0x7)) {
+ u64 rsvd_bits = spte & ept_rsvd_mask(spte, level);
+
+ if (rsvd_bits != 0) {
+ printk(KERN_ERR "%s: rsvd_bits = 0x%llx\n",
+ __func__, rsvd_bits);
+ WARN_ON(1);
+ }
+
+ if (level == 1 || (level == 2 && (spte & (1ULL << 7)))) {
+ u64 ept_mem_type = (spte & 0x38) >> 3;
+
+ if (ept_mem_type == 2 || ept_mem_type == 3 ||
+ ept_mem_type == 7) {
+ printk(KERN_ERR "%s: ept_mem_type=0x%llx\n",
+ __func__, ept_mem_type);
+ WARN_ON(1);
+ }
+ }
+ }
+}
+
+static int handle_ept_misconfig(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u64 sptes[4];
+ int nr_sptes, i;
+ gpa_t gpa;
+
+ gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS);
+
+ printk(KERN_ERR "EPT: Misconfiguration.\n");
+ printk(KERN_ERR "EPT: GPA: 0x%llx\n", gpa);
+
+ nr_sptes = kvm_mmu_get_spte_hierarchy(vcpu, gpa, sptes);
+
+ for (i = PT64_ROOT_LEVEL; i > PT64_ROOT_LEVEL - nr_sptes; --i)
+ ept_misconfig_inspect_spte(vcpu, sptes[i-1], i);
+
+ kvm_run->exit_reason = KVM_EXIT_UNKNOWN;
+ kvm_run->hw.hardware_exit_reason = EXIT_REASON_EPT_MISCONFIG;
+
+ return 0;
+}
+
static int handle_nmi_window(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
u32 cpu_based_vm_exec_control;
@@ -3217,8 +3391,9 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu,
[EXIT_REASON_APIC_ACCESS] = handle_apic_access,
[EXIT_REASON_WBINVD] = handle_wbinvd,
[EXIT_REASON_TASK_SWITCH] = handle_task_switch,
- [EXIT_REASON_EPT_VIOLATION] = handle_ept_violation,
[EXIT_REASON_MCE_DURING_VMENTRY] = handle_machine_check,
+ [EXIT_REASON_EPT_VIOLATION] = handle_ept_violation,
+ [EXIT_REASON_EPT_MISCONFIG] = handle_ept_misconfig,
};
static const int kvm_vmx_max_exit_handlers =
@@ -3234,8 +3409,7 @@ static int vmx_handle_exit(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
u32 exit_reason = vmx->exit_reason;
u32 vectoring_info = vmx->idt_vectoring_info;
- KVMTRACE_3D(VMEXIT, vcpu, exit_reason, (u32)kvm_rip_read(vcpu),
- (u32)((u64)kvm_rip_read(vcpu) >> 32), entryexit);
+ trace_kvm_exit(exit_reason, kvm_rip_read(vcpu));
/* If we need to emulate an MMIO from handle_invalid_guest_state
* we just return 0 */
@@ -3247,10 +3421,8 @@ static int vmx_handle_exit(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
/* Access CR3 don't cause VMExit in paging mode, so we need
* to sync with guest real CR3. */
- if (enable_ept && is_paging(vcpu)) {
+ if (enable_ept && is_paging(vcpu))
vcpu->arch.cr3 = vmcs_readl(GUEST_CR3);
- ept_load_pdptrs(vcpu);
- }
if (unlikely(vmx->fail)) {
kvm_run->exit_reason = KVM_EXIT_FAIL_ENTRY;
@@ -3326,10 +3498,8 @@ static void vmx_complete_interrupts(struct vcpu_vmx *vmx)
/* We need to handle NMIs before interrupts are enabled */
if ((exit_intr_info & INTR_INFO_INTR_TYPE_MASK) == INTR_TYPE_NMI_INTR &&
- (exit_intr_info & INTR_INFO_VALID_MASK)) {
- KVMTRACE_0D(NMI, &vmx->vcpu, handler);
+ (exit_intr_info & INTR_INFO_VALID_MASK))
asm("int $2");
- }
idtv_info_valid = idt_vectoring_info & VECTORING_INFO_VALID_MASK;
@@ -3434,6 +3604,10 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
+ if (enable_ept && is_paging(vcpu)) {
+ vmcs_writel(GUEST_CR3, vcpu->arch.cr3);
+ ept_load_pdptrs(vcpu);
+ }
/* Record the guest's net vcpu time for enforced NMI injections. */
if (unlikely(!cpu_has_virtual_nmis() && vmx->soft_vnmi_blocked))
vmx->entry_time = ktime_get();
@@ -3449,12 +3623,21 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
if (test_bit(VCPU_REGS_RIP, (unsigned long *)&vcpu->arch.regs_dirty))
vmcs_writel(GUEST_RIP, vcpu->arch.regs[VCPU_REGS_RIP]);
+ /* When single-stepping over STI and MOV SS, we must clear the
+ * corresponding interruptibility bits in the guest state. Otherwise
+ * vmentry fails as it then expects bit 14 (BS) in pending debug
+ * exceptions being set, but that's not correct for the guest debugging
+ * case. */
+ if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
+ vmx_set_interrupt_shadow(vcpu, 0);
+
/*
* Loading guest fpu may have cleared host cr0.ts
*/
vmcs_writel(HOST_CR0, read_cr0());
- set_debugreg(vcpu->arch.dr6, 6);
+ if (vcpu->arch.switch_db_regs)
+ set_debugreg(vcpu->arch.dr6, 6);
asm(
/* Store host registers */
@@ -3465,11 +3648,16 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
"mov %%"R"sp, %c[host_rsp](%0) \n\t"
__ex(ASM_VMX_VMWRITE_RSP_RDX) "\n\t"
"1: \n\t"
+ /* Reload cr2 if changed */
+ "mov %c[cr2](%0), %%"R"ax \n\t"
+ "mov %%cr2, %%"R"dx \n\t"
+ "cmp %%"R"ax, %%"R"dx \n\t"
+ "je 2f \n\t"
+ "mov %%"R"ax, %%cr2 \n\t"
+ "2: \n\t"
/* Check if vmlaunch of vmresume is needed */
"cmpl $0, %c[launched](%0) \n\t"
/* Load guest registers. Don't clobber flags. */
- "mov %c[cr2](%0), %%"R"ax \n\t"
- "mov %%"R"ax, %%cr2 \n\t"
"mov %c[rax](%0), %%"R"ax \n\t"
"mov %c[rbx](%0), %%"R"bx \n\t"
"mov %c[rdx](%0), %%"R"dx \n\t"
@@ -3547,10 +3735,12 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
#endif
);
- vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP));
+ vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP)
+ | (1 << VCPU_EXREG_PDPTR));
vcpu->arch.regs_dirty = 0;
- get_debugreg(vcpu->arch.dr6, 6);
+ if (vcpu->arch.switch_db_regs)
+ get_debugreg(vcpu->arch.dr6, 6);
vmx->idt_vectoring_info = vmcs_read32(IDT_VECTORING_INFO_FIELD);
if (vmx->rmode.irq.pending)
@@ -3633,9 +3823,13 @@ static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id)
if (alloc_apic_access_page(kvm) != 0)
goto free_vmcs;
- if (enable_ept)
+ if (enable_ept) {
+ if (!kvm->arch.ept_identity_map_addr)
+ kvm->arch.ept_identity_map_addr =
+ VMX_EPT_IDENTITY_PAGETABLE_ADDR;
if (alloc_identity_pagetable(kvm) != 0)
goto free_vmcs;
+ }
return &vmx->vcpu;
@@ -3699,6 +3893,34 @@ static u64 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio)
return ret;
}
+static const struct trace_print_flags vmx_exit_reasons_str[] = {
+ { EXIT_REASON_EXCEPTION_NMI, "exception" },
+ { EXIT_REASON_EXTERNAL_INTERRUPT, "ext_irq" },
+ { EXIT_REASON_TRIPLE_FAULT, "triple_fault" },
+ { EXIT_REASON_NMI_WINDOW, "nmi_window" },
+ { EXIT_REASON_IO_INSTRUCTION, "io_instruction" },
+ { EXIT_REASON_CR_ACCESS, "cr_access" },
+ { EXIT_REASON_DR_ACCESS, "dr_access" },
+ { EXIT_REASON_CPUID, "cpuid" },
+ { EXIT_REASON_MSR_READ, "rdmsr" },
+ { EXIT_REASON_MSR_WRITE, "wrmsr" },
+ { EXIT_REASON_PENDING_INTERRUPT, "interrupt_window" },
+ { EXIT_REASON_HLT, "halt" },
+ { EXIT_REASON_INVLPG, "invlpg" },
+ { EXIT_REASON_VMCALL, "hypercall" },
+ { EXIT_REASON_TPR_BELOW_THRESHOLD, "tpr_below_thres" },
+ { EXIT_REASON_APIC_ACCESS, "apic_access" },
+ { EXIT_REASON_WBINVD, "wbinvd" },
+ { EXIT_REASON_TASK_SWITCH, "task_switch" },
+ { EXIT_REASON_EPT_VIOLATION, "ept_violation" },
+ { -1, NULL }
+};
+
+static bool vmx_gb_page_enable(void)
+{
+ return false;
+}
+
static struct kvm_x86_ops vmx_x86_ops = {
.cpu_has_kvm_support = cpu_has_kvm_support,
.disabled_by_bios = vmx_disabled_by_bios,
@@ -3758,6 +3980,9 @@ static struct kvm_x86_ops vmx_x86_ops = {
.set_tss_addr = vmx_set_tss_addr,
.get_tdp_level = get_ept_level,
.get_mt_mask = vmx_get_mt_mask,
+
+ .exit_reasons_str = vmx_exit_reasons_str,
+ .gb_page_enable = vmx_gb_page_enable,
};
static int __init vmx_init(void)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 633ccc7400a..be451ee4424 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -37,11 +37,16 @@
#include <linux/iommu.h>
#include <linux/intel-iommu.h>
#include <linux/cpufreq.h>
+#include <trace/events/kvm.h>
+#undef TRACE_INCLUDE_FILE
+#define CREATE_TRACE_POINTS
+#include "trace.h"
#include <asm/uaccess.h>
#include <asm/msr.h>
#include <asm/desc.h>
#include <asm/mtrr.h>
+#include <asm/mce.h>
#define MAX_IO_MSRS 256
#define CR0_RESERVED_BITS \
@@ -55,6 +60,10 @@
| X86_CR4_OSXMMEXCPT | X86_CR4_VMXE))
#define CR8_RESERVED_BITS (~(unsigned long)X86_CR8_TPR)
+
+#define KVM_MAX_MCE_BANKS 32
+#define KVM_MCE_CAP_SUPPORTED MCG_CTL_P
+
/* EFER defaults:
* - enable syscall per default because its emulated by KVM
* - enable LME and LMA per default on 64 bit KVM
@@ -68,14 +77,16 @@ static u64 __read_mostly efer_reserved_bits = 0xfffffffffffffffeULL;
#define VM_STAT(x) offsetof(struct kvm, stat.x), KVM_STAT_VM
#define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU
+static void update_cr8_intercept(struct kvm_vcpu *vcpu);
static int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid,
struct kvm_cpuid_entry2 __user *entries);
-struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,
- u32 function, u32 index);
struct kvm_x86_ops *kvm_x86_ops;
EXPORT_SYMBOL_GPL(kvm_x86_ops);
+int ignore_msrs = 0;
+module_param_named(ignore_msrs, ignore_msrs, bool, S_IRUGO | S_IWUSR);
+
struct kvm_stats_debugfs_item debugfs_entries[] = {
{ "pf_fixed", VCPU_STAT(pf_fixed) },
{ "pf_guest", VCPU_STAT(pf_guest) },
@@ -122,18 +133,16 @@ unsigned long segment_base(u16 selector)
if (selector == 0)
return 0;
- asm("sgdt %0" : "=m"(gdt));
+ kvm_get_gdt(&gdt);
table_base = gdt.base;
if (selector & 4) { /* from ldt */
- u16 ldt_selector;
+ u16 ldt_selector = kvm_read_ldt();
- asm("sldt %0" : "=g"(ldt_selector));
table_base = segment_base(ldt_selector);
}
d = (struct desc_struct *)(table_base + (selector & ~7));
- v = d->base0 | ((unsigned long)d->base1 << 16) |
- ((unsigned long)d->base2 << 24);
+ v = get_desc_base(d);
#ifdef CONFIG_X86_64
if (d->s == 0 && (d->type == 2 || d->type == 9 || d->type == 11))
v |= ((unsigned long)((struct ldttss_desc64 *)d)->base3) << 32;
@@ -176,16 +185,22 @@ void kvm_inject_page_fault(struct kvm_vcpu *vcpu, unsigned long addr,
++vcpu->stat.pf_guest;
if (vcpu->arch.exception.pending) {
- if (vcpu->arch.exception.nr == PF_VECTOR) {
- printk(KERN_DEBUG "kvm: inject_page_fault:"
- " double fault 0x%lx\n", addr);
- vcpu->arch.exception.nr = DF_VECTOR;
- vcpu->arch.exception.error_code = 0;
- } else if (vcpu->arch.exception.nr == DF_VECTOR) {
+ switch(vcpu->arch.exception.nr) {
+ case DF_VECTOR:
/* triple fault -> shutdown */
set_bit(KVM_REQ_TRIPLE_FAULT, &vcpu->requests);
+ return;
+ case PF_VECTOR:
+ vcpu->arch.exception.nr = DF_VECTOR;
+ vcpu->arch.exception.error_code = 0;
+ return;
+ default:
+ /* replace previous exception with a new one in a hope
+ that instruction re-execution will regenerate lost
+ exception */
+ vcpu->arch.exception.pending = false;
+ break;
}
- return;
}
vcpu->arch.cr2 = addr;
kvm_queue_exception_e(vcpu, PF_VECTOR, error_code);
@@ -207,12 +222,18 @@ void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code)
}
EXPORT_SYMBOL_GPL(kvm_queue_exception_e);
-static void __queue_exception(struct kvm_vcpu *vcpu)
+/*
+ * Checks if cpl <= required_cpl; if true, return true. Otherwise queue
+ * a #GP and return false.
+ */
+bool kvm_require_cpl(struct kvm_vcpu *vcpu, int required_cpl)
{
- kvm_x86_ops->queue_exception(vcpu, vcpu->arch.exception.nr,
- vcpu->arch.exception.has_error_code,
- vcpu->arch.exception.error_code);
+ if (kvm_x86_ops->get_cpl(vcpu) <= required_cpl)
+ return true;
+ kvm_queue_exception_e(vcpu, GP_VECTOR, 0);
+ return false;
}
+EXPORT_SYMBOL_GPL(kvm_require_cpl);
/*
* Load the pae pdptrs. Return true is they are all valid.
@@ -232,7 +253,7 @@ int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3)
goto out;
}
for (i = 0; i < ARRAY_SIZE(pdpte); ++i) {
- if (is_present_pte(pdpte[i]) &&
+ if (is_present_gpte(pdpte[i]) &&
(pdpte[i] & vcpu->arch.mmu.rsvd_bits_mask[0][2])) {
ret = 0;
goto out;
@@ -241,6 +262,10 @@ int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3)
ret = 1;
memcpy(vcpu->arch.pdptrs, pdpte, sizeof(vcpu->arch.pdptrs));
+ __set_bit(VCPU_EXREG_PDPTR,
+ (unsigned long *)&vcpu->arch.regs_avail);
+ __set_bit(VCPU_EXREG_PDPTR,
+ (unsigned long *)&vcpu->arch.regs_dirty);
out:
return ret;
@@ -256,6 +281,10 @@ static bool pdptrs_changed(struct kvm_vcpu *vcpu)
if (is_long_mode(vcpu) || !is_pae(vcpu))
return false;
+ if (!test_bit(VCPU_EXREG_PDPTR,
+ (unsigned long *)&vcpu->arch.regs_avail))
+ return true;
+
r = kvm_read_guest(vcpu->kvm, vcpu->arch.cr3 & ~31u, pdpte, sizeof(pdpte));
if (r < 0)
goto out;
@@ -328,9 +357,6 @@ EXPORT_SYMBOL_GPL(kvm_set_cr0);
void kvm_lmsw(struct kvm_vcpu *vcpu, unsigned long msw)
{
kvm_set_cr0(vcpu, (vcpu->arch.cr0 & ~0x0ful) | (msw & 0x0f));
- KVMTRACE_1D(LMSW, vcpu,
- (u32)((vcpu->arch.cr0 & ~0x0ful) | (msw & 0x0f)),
- handler);
}
EXPORT_SYMBOL_GPL(kvm_lmsw);
@@ -466,7 +492,7 @@ static u32 msrs_to_save[] = {
#ifdef CONFIG_X86_64
MSR_CSTAR, MSR_KERNEL_GS_BASE, MSR_SYSCALL_MASK, MSR_LSTAR,
#endif
- MSR_IA32_TIME_STAMP_COUNTER, MSR_KVM_SYSTEM_TIME, MSR_KVM_WALL_CLOCK,
+ MSR_IA32_TSC, MSR_KVM_SYSTEM_TIME, MSR_KVM_WALL_CLOCK,
MSR_IA32_PERF_STATUS, MSR_IA32_CR_PAT, MSR_VM_HSAVE_PA
};
@@ -644,8 +670,7 @@ static void kvm_write_guest_time(struct kvm_vcpu *v)
/* Keep irq disabled to prevent changes to the clock */
local_irq_save(flags);
- kvm_get_msr(v, MSR_IA32_TIME_STAMP_COUNTER,
- &vcpu->hv_clock.tsc_timestamp);
+ kvm_get_msr(v, MSR_IA32_TSC, &vcpu->hv_clock.tsc_timestamp);
ktime_get_ts(&ts);
local_irq_restore(flags);
@@ -778,23 +803,60 @@ static int set_msr_mtrr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
return 0;
}
+static int set_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 data)
+{
+ u64 mcg_cap = vcpu->arch.mcg_cap;
+ unsigned bank_num = mcg_cap & 0xff;
+
+ switch (msr) {
+ case MSR_IA32_MCG_STATUS:
+ vcpu->arch.mcg_status = data;
+ break;
+ case MSR_IA32_MCG_CTL:
+ if (!(mcg_cap & MCG_CTL_P))
+ return 1;
+ if (data != 0 && data != ~(u64)0)
+ return -1;
+ vcpu->arch.mcg_ctl = data;
+ break;
+ default:
+ if (msr >= MSR_IA32_MC0_CTL &&
+ msr < MSR_IA32_MC0_CTL + 4 * bank_num) {
+ u32 offset = msr - MSR_IA32_MC0_CTL;
+ /* only 0 or all 1s can be written to IA32_MCi_CTL */
+ if ((offset & 0x3) == 0 &&
+ data != 0 && data != ~(u64)0)
+ return -1;
+ vcpu->arch.mce_banks[offset] = data;
+ break;
+ }
+ return 1;
+ }
+ return 0;
+}
+
int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)
{
switch (msr) {
case MSR_EFER:
set_efer(vcpu, data);
break;
- case MSR_IA32_MC0_STATUS:
- pr_unimpl(vcpu, "%s: MSR_IA32_MC0_STATUS 0x%llx, nop\n",
- __func__, data);
+ case MSR_K7_HWCR:
+ data &= ~(u64)0x40; /* ignore flush filter disable */
+ if (data != 0) {
+ pr_unimpl(vcpu, "unimplemented HWCR wrmsr: 0x%llx\n",
+ data);
+ return 1;
+ }
break;
- case MSR_IA32_MCG_STATUS:
- pr_unimpl(vcpu, "%s: MSR_IA32_MCG_STATUS 0x%llx, nop\n",
- __func__, data);
+ case MSR_FAM10H_MMIO_CONF_BASE:
+ if (data != 0) {
+ pr_unimpl(vcpu, "unimplemented MMIO_CONF_BASE wrmsr: "
+ "0x%llx\n", data);
+ return 1;
+ }
break;
- case MSR_IA32_MCG_CTL:
- pr_unimpl(vcpu, "%s: MSR_IA32_MCG_CTL 0x%llx, nop\n",
- __func__, data);
+ case MSR_AMD64_NB_CFG:
break;
case MSR_IA32_DEBUGCTLMSR:
if (!data) {
@@ -811,12 +873,15 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)
case MSR_IA32_UCODE_REV:
case MSR_IA32_UCODE_WRITE:
case MSR_VM_HSAVE_PA:
+ case MSR_AMD64_PATCH_LOADER:
break;
case 0x200 ... 0x2ff:
return set_msr_mtrr(vcpu, msr, data);
case MSR_IA32_APICBASE:
kvm_set_apic_base(vcpu, data);
break;
+ case APIC_BASE_MSR ... APIC_BASE_MSR + 0x3ff:
+ return kvm_x2apic_msr_write(vcpu, msr, data);
case MSR_IA32_MISC_ENABLE:
vcpu->arch.ia32_misc_enable_msr = data;
break;
@@ -850,9 +915,50 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)
kvm_request_guest_time_update(vcpu);
break;
}
+ case MSR_IA32_MCG_CTL:
+ case MSR_IA32_MCG_STATUS:
+ case MSR_IA32_MC0_CTL ... MSR_IA32_MC0_CTL + 4 * KVM_MAX_MCE_BANKS - 1:
+ return set_msr_mce(vcpu, msr, data);
+
+ /* Performance counters are not protected by a CPUID bit,
+ * so we should check all of them in the generic path for the sake of
+ * cross vendor migration.
+ * Writing a zero into the event select MSRs disables them,
+ * which we perfectly emulate ;-). Any other value should be at least
+ * reported, some guests depend on them.
+ */
+ case MSR_P6_EVNTSEL0:
+ case MSR_P6_EVNTSEL1:
+ case MSR_K7_EVNTSEL0:
+ case MSR_K7_EVNTSEL1:
+ case MSR_K7_EVNTSEL2:
+ case MSR_K7_EVNTSEL3:
+ if (data != 0)
+ pr_unimpl(vcpu, "unimplemented perfctr wrmsr: "
+ "0x%x data 0x%llx\n", msr, data);
+ break;
+ /* at least RHEL 4 unconditionally writes to the perfctr registers,
+ * so we ignore writes to make it happy.
+ */
+ case MSR_P6_PERFCTR0:
+ case MSR_P6_PERFCTR1:
+ case MSR_K7_PERFCTR0:
+ case MSR_K7_PERFCTR1:
+ case MSR_K7_PERFCTR2:
+ case MSR_K7_PERFCTR3:
+ pr_unimpl(vcpu, "unimplemented perfctr wrmsr: "
+ "0x%x data 0x%llx\n", msr, data);
+ break;
default:
- pr_unimpl(vcpu, "unhandled wrmsr: 0x%x data %llx\n", msr, data);
- return 1;
+ if (!ignore_msrs) {
+ pr_unimpl(vcpu, "unhandled wrmsr: 0x%x data %llx\n",
+ msr, data);
+ return 1;
+ } else {
+ pr_unimpl(vcpu, "ignored wrmsr: 0x%x data %llx\n",
+ msr, data);
+ break;
+ }
}
return 0;
}
@@ -905,26 +1011,47 @@ static int get_msr_mtrr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
return 0;
}
-int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
+static int get_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
{
u64 data;
+ u64 mcg_cap = vcpu->arch.mcg_cap;
+ unsigned bank_num = mcg_cap & 0xff;
switch (msr) {
- case 0xc0010010: /* SYSCFG */
- case 0xc0010015: /* HWCR */
- case MSR_IA32_PLATFORM_ID:
case MSR_IA32_P5_MC_ADDR:
case MSR_IA32_P5_MC_TYPE:
- case MSR_IA32_MC0_CTL:
- case MSR_IA32_MCG_STATUS:
+ data = 0;
+ break;
case MSR_IA32_MCG_CAP:
+ data = vcpu->arch.mcg_cap;
+ break;
case MSR_IA32_MCG_CTL:
- case MSR_IA32_MC0_MISC:
- case MSR_IA32_MC0_MISC+4:
- case MSR_IA32_MC0_MISC+8:
- case MSR_IA32_MC0_MISC+12:
- case MSR_IA32_MC0_MISC+16:
- case MSR_IA32_MC0_MISC+20:
+ if (!(mcg_cap & MCG_CTL_P))
+ return 1;
+ data = vcpu->arch.mcg_ctl;
+ break;
+ case MSR_IA32_MCG_STATUS:
+ data = vcpu->arch.mcg_status;
+ break;
+ default:
+ if (msr >= MSR_IA32_MC0_CTL &&
+ msr < MSR_IA32_MC0_CTL + 4 * bank_num) {
+ u32 offset = msr - MSR_IA32_MC0_CTL;
+ data = vcpu->arch.mce_banks[offset];
+ break;
+ }
+ return 1;
+ }
+ *pdata = data;
+ return 0;
+}
+
+int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
+{
+ u64 data;
+
+ switch (msr) {
+ case MSR_IA32_PLATFORM_ID:
case MSR_IA32_UCODE_REV:
case MSR_IA32_EBL_CR_POWERON:
case MSR_IA32_DEBUGCTLMSR:
@@ -932,10 +1059,18 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
case MSR_IA32_LASTBRANCHTOIP:
case MSR_IA32_LASTINTFROMIP:
case MSR_IA32_LASTINTTOIP:
+ case MSR_K8_SYSCFG:
+ case MSR_K7_HWCR:
case MSR_VM_HSAVE_PA:
+ case MSR_P6_PERFCTR0:
+ case MSR_P6_PERFCTR1:
case MSR_P6_EVNTSEL0:
case MSR_P6_EVNTSEL1:
case MSR_K7_EVNTSEL0:
+ case MSR_K7_PERFCTR0:
+ case MSR_K8_INT_PENDING_MSG:
+ case MSR_AMD64_NB_CFG:
+ case MSR_FAM10H_MMIO_CONF_BASE:
data = 0;
break;
case MSR_MTRRcap:
@@ -949,6 +1084,9 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
case MSR_IA32_APICBASE:
data = kvm_get_apic_base(vcpu);
break;
+ case APIC_BASE_MSR ... APIC_BASE_MSR + 0x3ff:
+ return kvm_x2apic_msr_read(vcpu, msr, pdata);
+ break;
case MSR_IA32_MISC_ENABLE:
data = vcpu->arch.ia32_misc_enable_msr;
break;
@@ -967,9 +1105,22 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
case MSR_KVM_SYSTEM_TIME:
data = vcpu->arch.time;
break;
+ case MSR_IA32_P5_MC_ADDR:
+ case MSR_IA32_P5_MC_TYPE:
+ case MSR_IA32_MCG_CAP:
+ case MSR_IA32_MCG_CTL:
+ case MSR_IA32_MCG_STATUS:
+ case MSR_IA32_MC0_CTL ... MSR_IA32_MC0_CTL + 4 * KVM_MAX_MCE_BANKS - 1:
+ return get_msr_mce(vcpu, msr, pdata);
default:
- pr_unimpl(vcpu, "unhandled rdmsr: 0x%x\n", msr);
- return 1;
+ if (!ignore_msrs) {
+ pr_unimpl(vcpu, "unhandled rdmsr: 0x%x\n", msr);
+ return 1;
+ } else {
+ pr_unimpl(vcpu, "ignored rdmsr: 0x%x\n", msr);
+ data = 0;
+ }
+ break;
}
*pdata = data;
return 0;
@@ -1068,6 +1219,11 @@ int kvm_dev_ioctl_check_extension(long ext)
case KVM_CAP_REINJECT_CONTROL:
case KVM_CAP_IRQ_INJECT_STATUS:
case KVM_CAP_ASSIGN_DEV_IRQ:
+ case KVM_CAP_IRQFD:
+ case KVM_CAP_IOEVENTFD:
+ case KVM_CAP_PIT2:
+ case KVM_CAP_PIT_STATE2:
+ case KVM_CAP_SET_IDENTITY_MAP_ADDR:
r = 1;
break;
case KVM_CAP_COALESCED_MMIO:
@@ -1088,6 +1244,9 @@ int kvm_dev_ioctl_check_extension(long ext)
case KVM_CAP_IOMMU:
r = iommu_found();
break;
+ case KVM_CAP_MCE:
+ r = KVM_MAX_MCE_BANKS;
+ break;
default:
r = 0;
break;
@@ -1147,6 +1306,16 @@ long kvm_arch_dev_ioctl(struct file *filp,
r = 0;
break;
}
+ case KVM_X86_GET_MCE_CAP_SUPPORTED: {
+ u64 mce_cap;
+
+ mce_cap = KVM_MCE_CAP_SUPPORTED;
+ r = -EFAULT;
+ if (copy_to_user(argp, &mce_cap, sizeof mce_cap))
+ goto out;
+ r = 0;
+ break;
+ }
default:
r = -EINVAL;
}
@@ -1227,6 +1396,7 @@ static int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu *vcpu,
vcpu->arch.cpuid_nent = cpuid->nent;
cpuid_fix_nx_cap(vcpu);
r = 0;
+ kvm_apic_set_version(vcpu);
out_free:
vfree(cpuid_entries);
@@ -1248,6 +1418,7 @@ static int kvm_vcpu_ioctl_set_cpuid2(struct kvm_vcpu *vcpu,
cpuid->nent * sizeof(struct kvm_cpuid_entry2)))
goto out;
vcpu->arch.cpuid_nent = cpuid->nent;
+ kvm_apic_set_version(vcpu);
return 0;
out:
@@ -1290,6 +1461,7 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
u32 index, int *nent, int maxnent)
{
unsigned f_nx = is_efer_nx() ? F(NX) : 0;
+ unsigned f_gbpages = kvm_x86_ops->gb_page_enable() ? F(GBPAGES) : 0;
#ifdef CONFIG_X86_64
unsigned f_lm = F(LM);
#else
@@ -1314,7 +1486,7 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
F(MTRR) | F(PGE) | F(MCA) | F(CMOV) |
F(PAT) | F(PSE36) | 0 /* Reserved */ |
f_nx | 0 /* Reserved */ | F(MMXEXT) | F(MMX) |
- F(FXSR) | F(FXSR_OPT) | 0 /* GBPAGES */ | 0 /* RDTSCP */ |
+ F(FXSR) | F(FXSR_OPT) | f_gbpages | 0 /* RDTSCP */ |
0 /* Reserved */ | f_lm | F(3DNOWEXT) | F(3DNOW);
/* cpuid 1.ecx */
const u32 kvm_supported_word4_x86_features =
@@ -1323,7 +1495,7 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
0 /* TM2 */ | F(SSSE3) | 0 /* CNXT-ID */ | 0 /* Reserved */ |
0 /* Reserved */ | F(CX16) | 0 /* xTPR Update, PDCM */ |
0 /* Reserved, DCA */ | F(XMM4_1) |
- F(XMM4_2) | 0 /* x2APIC */ | F(MOVBE) | F(POPCNT) |
+ F(XMM4_2) | F(X2APIC) | F(MOVBE) | F(POPCNT) |
0 /* Reserved, XSAVE, OSXSAVE */;
/* cpuid 0x80000001.ecx */
const u32 kvm_supported_word6_x86_features =
@@ -1344,6 +1516,9 @@ static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
case 1:
entry->edx &= kvm_supported_word0_x86_features;
entry->ecx &= kvm_supported_word4_x86_features;
+ /* we support x2apic emulation even if host does not support
+ * it since we emulate x2apic in software */
+ entry->ecx |= F(X2APIC);
break;
/* function 2 entries are STATEFUL. That is, repeated cpuid commands
* may return different values. This forces us to get_cpu() before
@@ -1435,6 +1610,10 @@ static int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid,
for (func = 0x80000001; func <= limit && nent < cpuid->nent; ++func)
do_cpuid_ent(&cpuid_entries[nent], func, 0,
&nent, cpuid->nent);
+ r = -E2BIG;
+ if (nent >= cpuid->nent)
+ goto out_free;
+
r = -EFAULT;
if (copy_to_user(entries, cpuid_entries,
nent * sizeof(struct kvm_cpuid_entry2)))
@@ -1464,6 +1643,7 @@ static int kvm_vcpu_ioctl_set_lapic(struct kvm_vcpu *vcpu,
vcpu_load(vcpu);
memcpy(vcpu->arch.apic->regs, s->regs, sizeof *s);
kvm_apic_post_state_restore(vcpu);
+ update_cr8_intercept(vcpu);
vcpu_put(vcpu);
return 0;
@@ -1503,6 +1683,80 @@ static int vcpu_ioctl_tpr_access_reporting(struct kvm_vcpu *vcpu,
return 0;
}
+static int kvm_vcpu_ioctl_x86_setup_mce(struct kvm_vcpu *vcpu,
+ u64 mcg_cap)
+{
+ int r;
+ unsigned bank_num = mcg_cap & 0xff, bank;
+
+ r = -EINVAL;
+ if (!bank_num)
+ goto out;
+ if (mcg_cap & ~(KVM_MCE_CAP_SUPPORTED | 0xff | 0xff0000))
+ goto out;
+ r = 0;
+ vcpu->arch.mcg_cap = mcg_cap;
+ /* Init IA32_MCG_CTL to all 1s */
+ if (mcg_cap & MCG_CTL_P)
+ vcpu->arch.mcg_ctl = ~(u64)0;
+ /* Init IA32_MCi_CTL to all 1s */
+ for (bank = 0; bank < bank_num; bank++)
+ vcpu->arch.mce_banks[bank*4] = ~(u64)0;
+out:
+ return r;
+}
+
+static int kvm_vcpu_ioctl_x86_set_mce(struct kvm_vcpu *vcpu,
+ struct kvm_x86_mce *mce)
+{
+ u64 mcg_cap = vcpu->arch.mcg_cap;
+ unsigned bank_num = mcg_cap & 0xff;
+ u64 *banks = vcpu->arch.mce_banks;
+
+ if (mce->bank >= bank_num || !(mce->status & MCI_STATUS_VAL))
+ return -EINVAL;
+ /*
+ * if IA32_MCG_CTL is not all 1s, the uncorrected error
+ * reporting is disabled
+ */
+ if ((mce->status & MCI_STATUS_UC) && (mcg_cap & MCG_CTL_P) &&
+ vcpu->arch.mcg_ctl != ~(u64)0)
+ return 0;
+ banks += 4 * mce->bank;
+ /*
+ * if IA32_MCi_CTL is not all 1s, the uncorrected error
+ * reporting is disabled for the bank
+ */
+ if ((mce->status & MCI_STATUS_UC) && banks[0] != ~(u64)0)
+ return 0;
+ if (mce->status & MCI_STATUS_UC) {
+ if ((vcpu->arch.mcg_status & MCG_STATUS_MCIP) ||
+ !(vcpu->arch.cr4 & X86_CR4_MCE)) {
+ printk(KERN_DEBUG "kvm: set_mce: "
+ "injects mce exception while "
+ "previous one is in progress!\n");
+ set_bit(KVM_REQ_TRIPLE_FAULT, &vcpu->requests);
+ return 0;
+ }
+ if (banks[1] & MCI_STATUS_VAL)
+ mce->status |= MCI_STATUS_OVER;
+ banks[2] = mce->addr;
+ banks[3] = mce->misc;
+ vcpu->arch.mcg_status = mce->mcg_status;
+ banks[1] = mce->status;
+ kvm_queue_exception(vcpu, MC_VECTOR);
+ } else if (!(banks[1] & MCI_STATUS_VAL)
+ || !(banks[1] & MCI_STATUS_UC)) {
+ if (banks[1] & MCI_STATUS_VAL)
+ mce->status |= MCI_STATUS_OVER;
+ banks[2] = mce->addr;
+ banks[3] = mce->misc;
+ banks[1] = mce->status;
+ } else
+ banks[1] |= MCI_STATUS_OVER;
+ return 0;
+}
+
long kvm_arch_vcpu_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
@@ -1636,6 +1890,24 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
kvm_lapic_set_vapic_addr(vcpu, va.vapic_addr);
break;
}
+ case KVM_X86_SETUP_MCE: {
+ u64 mcg_cap;
+
+ r = -EFAULT;
+ if (copy_from_user(&mcg_cap, argp, sizeof mcg_cap))
+ goto out;
+ r = kvm_vcpu_ioctl_x86_setup_mce(vcpu, mcg_cap);
+ break;
+ }
+ case KVM_X86_SET_MCE: {
+ struct kvm_x86_mce mce;
+
+ r = -EFAULT;
+ if (copy_from_user(&mce, argp, sizeof mce))
+ goto out;
+ r = kvm_vcpu_ioctl_x86_set_mce(vcpu, &mce);
+ break;
+ }
default:
r = -EINVAL;
}
@@ -1654,6 +1926,13 @@ static int kvm_vm_ioctl_set_tss_addr(struct kvm *kvm, unsigned long addr)
return ret;
}
+static int kvm_vm_ioctl_set_identity_map_addr(struct kvm *kvm,
+ u64 ident_addr)
+{
+ kvm->arch.ept_identity_map_addr = ident_addr;
+ return 0;
+}
+
static int kvm_vm_ioctl_set_nr_mmu_pages(struct kvm *kvm,
u32 kvm_nr_mmu_pages)
{
@@ -1775,19 +2054,25 @@ static int kvm_vm_ioctl_set_irqchip(struct kvm *kvm, struct kvm_irqchip *chip)
r = 0;
switch (chip->chip_id) {
case KVM_IRQCHIP_PIC_MASTER:
+ spin_lock(&pic_irqchip(kvm)->lock);
memcpy(&pic_irqchip(kvm)->pics[0],
&chip->chip.pic,
sizeof(struct kvm_pic_state));
+ spin_unlock(&pic_irqchip(kvm)->lock);
break;
case KVM_IRQCHIP_PIC_SLAVE:
+ spin_lock(&pic_irqchip(kvm)->lock);
memcpy(&pic_irqchip(kvm)->pics[1],
&chip->chip.pic,
sizeof(struct kvm_pic_state));
+ spin_unlock(&pic_irqchip(kvm)->lock);
break;
case KVM_IRQCHIP_IOAPIC:
+ mutex_lock(&kvm->irq_lock);
memcpy(ioapic_irqchip(kvm),
&chip->chip.ioapic,
sizeof(struct kvm_ioapic_state));
+ mutex_unlock(&kvm->irq_lock);
break;
default:
r = -EINVAL;
@@ -1801,7 +2086,9 @@ static int kvm_vm_ioctl_get_pit(struct kvm *kvm, struct kvm_pit_state *ps)
{
int r = 0;
+ mutex_lock(&kvm->arch.vpit->pit_state.lock);
memcpy(ps, &kvm->arch.vpit->pit_state, sizeof(struct kvm_pit_state));
+ mutex_unlock(&kvm->arch.vpit->pit_state.lock);
return r;
}
@@ -1809,8 +2096,39 @@ static int kvm_vm_ioctl_set_pit(struct kvm *kvm, struct kvm_pit_state *ps)
{
int r = 0;
+ mutex_lock(&kvm->arch.vpit->pit_state.lock);
memcpy(&kvm->arch.vpit->pit_state, ps, sizeof(struct kvm_pit_state));
- kvm_pit_load_count(kvm, 0, ps->channels[0].count);
+ kvm_pit_load_count(kvm, 0, ps->channels[0].count, 0);
+ mutex_unlock(&kvm->arch.vpit->pit_state.lock);
+ return r;
+}
+
+static int kvm_vm_ioctl_get_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps)
+{
+ int r = 0;
+
+ mutex_lock(&kvm->arch.vpit->pit_state.lock);
+ memcpy(ps->channels, &kvm->arch.vpit->pit_state.channels,
+ sizeof(ps->channels));
+ ps->flags = kvm->arch.vpit->pit_state.flags;
+ mutex_unlock(&kvm->arch.vpit->pit_state.lock);
+ return r;
+}
+
+static int kvm_vm_ioctl_set_pit2(struct kvm *kvm, struct kvm_pit_state2 *ps)
+{
+ int r = 0, start = 0;
+ u32 prev_legacy, cur_legacy;
+ mutex_lock(&kvm->arch.vpit->pit_state.lock);
+ prev_legacy = kvm->arch.vpit->pit_state.flags & KVM_PIT_FLAGS_HPET_LEGACY;
+ cur_legacy = ps->flags & KVM_PIT_FLAGS_HPET_LEGACY;
+ if (!prev_legacy && cur_legacy)
+ start = 1;
+ memcpy(&kvm->arch.vpit->pit_state.channels, &ps->channels,
+ sizeof(kvm->arch.vpit->pit_state.channels));
+ kvm->arch.vpit->pit_state.flags = ps->flags;
+ kvm_pit_load_count(kvm, 0, kvm->arch.vpit->pit_state.channels[0].count, start);
+ mutex_unlock(&kvm->arch.vpit->pit_state.lock);
return r;
}
@@ -1819,7 +2137,9 @@ static int kvm_vm_ioctl_reinject(struct kvm *kvm,
{
if (!kvm->arch.vpit)
return -ENXIO;
+ mutex_lock(&kvm->arch.vpit->pit_state.lock);
kvm->arch.vpit->pit_state.pit_timer.reinject = control->pit_reinject;
+ mutex_unlock(&kvm->arch.vpit->pit_state.lock);
return 0;
}
@@ -1845,7 +2165,6 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm,
spin_lock(&kvm->mmu_lock);
kvm_mmu_slot_remove_write_access(kvm, log->slot);
spin_unlock(&kvm->mmu_lock);
- kvm_flush_remote_tlbs(kvm);
memslot = &kvm->memslots[log->slot];
n = ALIGN(memslot->npages, BITS_PER_LONG) / 8;
memset(memslot->dirty_bitmap, 0, n);
@@ -1869,7 +2188,9 @@ long kvm_arch_vm_ioctl(struct file *filp,
*/
union {
struct kvm_pit_state ps;
+ struct kvm_pit_state2 ps2;
struct kvm_memory_alias alias;
+ struct kvm_pit_config pit_config;
} u;
switch (ioctl) {
@@ -1878,6 +2199,17 @@ long kvm_arch_vm_ioctl(struct file *filp,
if (r < 0)
goto out;
break;
+ case KVM_SET_IDENTITY_MAP_ADDR: {
+ u64 ident_addr;
+
+ r = -EFAULT;
+ if (copy_from_user(&ident_addr, argp, sizeof ident_addr))
+ goto out;
+ r = kvm_vm_ioctl_set_identity_map_addr(kvm, ident_addr);
+ if (r < 0)
+ goto out;
+ break;
+ }
case KVM_SET_MEMORY_REGION: {
struct kvm_memory_region kvm_mem;
struct kvm_userspace_memory_region kvm_userspace_mem;
@@ -1930,16 +2262,24 @@ long kvm_arch_vm_ioctl(struct file *filp,
}
break;
case KVM_CREATE_PIT:
- mutex_lock(&kvm->lock);
+ u.pit_config.flags = KVM_PIT_SPEAKER_DUMMY;
+ goto create_pit;
+ case KVM_CREATE_PIT2:
+ r = -EFAULT;
+ if (copy_from_user(&u.pit_config, argp,
+ sizeof(struct kvm_pit_config)))
+ goto out;
+ create_pit:
+ down_write(&kvm->slots_lock);
r = -EEXIST;
if (kvm->arch.vpit)
goto create_pit_unlock;
r = -ENOMEM;
- kvm->arch.vpit = kvm_create_pit(kvm);
+ kvm->arch.vpit = kvm_create_pit(kvm, u.pit_config.flags);
if (kvm->arch.vpit)
r = 0;
create_pit_unlock:
- mutex_unlock(&kvm->lock);
+ up_write(&kvm->slots_lock);
break;
case KVM_IRQ_LINE_STATUS:
case KVM_IRQ_LINE: {
@@ -1950,10 +2290,10 @@ long kvm_arch_vm_ioctl(struct file *filp,
goto out;
if (irqchip_in_kernel(kvm)) {
__s32 status;
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->irq_lock);
status = kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID,
irq_event.irq, irq_event.level);
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->irq_lock);
if (ioctl == KVM_IRQ_LINE_STATUS) {
irq_event.status = status;
if (copy_to_user(argp, &irq_event,
@@ -2042,6 +2382,32 @@ long kvm_arch_vm_ioctl(struct file *filp,
r = 0;
break;
}
+ case KVM_GET_PIT2: {
+ r = -ENXIO;
+ if (!kvm->arch.vpit)
+ goto out;
+ r = kvm_vm_ioctl_get_pit2(kvm, &u.ps2);
+ if (r)
+ goto out;
+ r = -EFAULT;
+ if (copy_to_user(argp, &u.ps2, sizeof(u.ps2)))
+ goto out;
+ r = 0;
+ break;
+ }
+ case KVM_SET_PIT2: {
+ r = -EFAULT;
+ if (copy_from_user(&u.ps2, argp, sizeof(u.ps2)))
+ goto out;
+ r = -ENXIO;
+ if (!kvm->arch.vpit)
+ goto out;
+ r = kvm_vm_ioctl_set_pit2(kvm, &u.ps2);
+ if (r)
+ goto out;
+ r = 0;
+ break;
+ }
case KVM_REINJECT_CONTROL: {
struct kvm_reinject_control control;
r = -EFAULT;
@@ -2075,35 +2441,23 @@ static void kvm_init_msr_list(void)
num_msrs_to_save = j;
}
-/*
- * Only apic need an MMIO device hook, so shortcut now..
- */
-static struct kvm_io_device *vcpu_find_pervcpu_dev(struct kvm_vcpu *vcpu,
- gpa_t addr, int len,
- int is_write)
+static int vcpu_mmio_write(struct kvm_vcpu *vcpu, gpa_t addr, int len,
+ const void *v)
{
- struct kvm_io_device *dev;
+ if (vcpu->arch.apic &&
+ !kvm_iodevice_write(&vcpu->arch.apic->dev, addr, len, v))
+ return 0;
- if (vcpu->arch.apic) {
- dev = &vcpu->arch.apic->dev;
- if (dev->in_range(dev, addr, len, is_write))
- return dev;
- }
- return NULL;
+ return kvm_io_bus_write(&vcpu->kvm->mmio_bus, addr, len, v);
}
-
-static struct kvm_io_device *vcpu_find_mmio_dev(struct kvm_vcpu *vcpu,
- gpa_t addr, int len,
- int is_write)
+static int vcpu_mmio_read(struct kvm_vcpu *vcpu, gpa_t addr, int len, void *v)
{
- struct kvm_io_device *dev;
+ if (vcpu->arch.apic &&
+ !kvm_iodevice_read(&vcpu->arch.apic->dev, addr, len, v))
+ return 0;
- dev = vcpu_find_pervcpu_dev(vcpu, addr, len, is_write);
- if (dev == NULL)
- dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr, len,
- is_write);
- return dev;
+ return kvm_io_bus_read(&vcpu->kvm->mmio_bus, addr, len, v);
}
static int kvm_read_guest_virt(gva_t addr, void *val, unsigned int bytes,
@@ -2172,11 +2526,12 @@ static int emulator_read_emulated(unsigned long addr,
unsigned int bytes,
struct kvm_vcpu *vcpu)
{
- struct kvm_io_device *mmio_dev;
gpa_t gpa;
if (vcpu->mmio_read_completed) {
memcpy(val, vcpu->mmio_data, bytes);
+ trace_kvm_mmio(KVM_TRACE_MMIO_READ, bytes,
+ vcpu->mmio_phys_addr, *(u64 *)val);
vcpu->mmio_read_completed = 0;
return X86EMUL_CONTINUE;
}
@@ -2197,14 +2552,12 @@ mmio:
/*
* Is this MMIO handled locally?
*/
- mutex_lock(&vcpu->kvm->lock);
- mmio_dev = vcpu_find_mmio_dev(vcpu, gpa, bytes, 0);
- if (mmio_dev) {
- kvm_iodevice_read(mmio_dev, gpa, bytes, val);
- mutex_unlock(&vcpu->kvm->lock);
+ if (!vcpu_mmio_read(vcpu, gpa, bytes, val)) {
+ trace_kvm_mmio(KVM_TRACE_MMIO_READ, bytes, gpa, *(u64 *)val);
return X86EMUL_CONTINUE;
}
- mutex_unlock(&vcpu->kvm->lock);
+
+ trace_kvm_mmio(KVM_TRACE_MMIO_READ_UNSATISFIED, bytes, gpa, 0);
vcpu->mmio_needed = 1;
vcpu->mmio_phys_addr = gpa;
@@ -2231,7 +2584,6 @@ static int emulator_write_emulated_onepage(unsigned long addr,
unsigned int bytes,
struct kvm_vcpu *vcpu)
{
- struct kvm_io_device *mmio_dev;
gpa_t gpa;
gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, addr);
@@ -2249,17 +2601,12 @@ static int emulator_write_emulated_onepage(unsigned long addr,
return X86EMUL_CONTINUE;
mmio:
+ trace_kvm_mmio(KVM_TRACE_MMIO_WRITE, bytes, gpa, *(u64 *)val);
/*
* Is this MMIO handled locally?
*/
- mutex_lock(&vcpu->kvm->lock);
- mmio_dev = vcpu_find_mmio_dev(vcpu, gpa, bytes, 1);
- if (mmio_dev) {
- kvm_iodevice_write(mmio_dev, gpa, bytes, val);
- mutex_unlock(&vcpu->kvm->lock);
+ if (!vcpu_mmio_write(vcpu, gpa, bytes, val))
return X86EMUL_CONTINUE;
- }
- mutex_unlock(&vcpu->kvm->lock);
vcpu->mmio_needed = 1;
vcpu->mmio_phys_addr = gpa;
@@ -2343,7 +2690,6 @@ int emulate_invlpg(struct kvm_vcpu *vcpu, gva_t address)
int emulate_clts(struct kvm_vcpu *vcpu)
{
- KVMTRACE_0D(CLTS, vcpu, handler);
kvm_x86_ops->set_cr0(vcpu, vcpu->arch.cr0 & ~X86_CR0_TS);
return X86EMUL_CONTINUE;
}
@@ -2420,7 +2766,7 @@ int emulate_instruction(struct kvm_vcpu *vcpu,
kvm_clear_exception_queue(vcpu);
vcpu->arch.mmio_fault_cr2 = cr2;
/*
- * TODO: fix x86_emulate.c to use guest_read/write_register
+ * TODO: fix emulate.c to use guest_read/write_register
* instead of direct ->regs accesses, can save hundred cycles
* on Intel for instructions that don't read/change RSP, for
* for example.
@@ -2444,14 +2790,33 @@ int emulate_instruction(struct kvm_vcpu *vcpu,
r = x86_decode_insn(&vcpu->arch.emulate_ctxt, &emulate_ops);
- /* Reject the instructions other than VMCALL/VMMCALL when
- * try to emulate invalid opcode */
+ /* Only allow emulation of specific instructions on #UD
+ * (namely VMMCALL, sysenter, sysexit, syscall)*/
c = &vcpu->arch.emulate_ctxt.decode;
- if ((emulation_type & EMULTYPE_TRAP_UD) &&
- (!(c->twobyte && c->b == 0x01 &&
- (c->modrm_reg == 0 || c->modrm_reg == 3) &&
- c->modrm_mod == 3 && c->modrm_rm == 1)))
- return EMULATE_FAIL;
+ if (emulation_type & EMULTYPE_TRAP_UD) {
+ if (!c->twobyte)
+ return EMULATE_FAIL;
+ switch (c->b) {
+ case 0x01: /* VMMCALL */
+ if (c->modrm_mod != 3 || c->modrm_rm != 1)
+ return EMULATE_FAIL;
+ break;
+ case 0x34: /* sysenter */
+ case 0x35: /* sysexit */
+ if (c->modrm_mod != 0 || c->modrm_rm != 0)
+ return EMULATE_FAIL;
+ break;
+ case 0x05: /* syscall */
+ if (c->modrm_mod != 0 || c->modrm_rm != 0)
+ return EMULATE_FAIL;
+ break;
+ default:
+ return EMULATE_FAIL;
+ }
+
+ if (!(c->modrm_reg == 0 || c->modrm_reg == 3))
+ return EMULATE_FAIL;
+ }
++vcpu->stat.insn_emulation;
if (r) {
@@ -2571,52 +2936,40 @@ int complete_pio(struct kvm_vcpu *vcpu)
return 0;
}
-static void kernel_pio(struct kvm_io_device *pio_dev,
- struct kvm_vcpu *vcpu,
- void *pd)
+static int kernel_pio(struct kvm_vcpu *vcpu, void *pd)
{
/* TODO: String I/O for in kernel device */
+ int r;
- mutex_lock(&vcpu->kvm->lock);
if (vcpu->arch.pio.in)
- kvm_iodevice_read(pio_dev, vcpu->arch.pio.port,
- vcpu->arch.pio.size,
- pd);
+ r = kvm_io_bus_read(&vcpu->kvm->pio_bus, vcpu->arch.pio.port,
+ vcpu->arch.pio.size, pd);
else
- kvm_iodevice_write(pio_dev, vcpu->arch.pio.port,
- vcpu->arch.pio.size,
- pd);
- mutex_unlock(&vcpu->kvm->lock);
+ r = kvm_io_bus_write(&vcpu->kvm->pio_bus, vcpu->arch.pio.port,
+ vcpu->arch.pio.size, pd);
+ return r;
}
-static void pio_string_write(struct kvm_io_device *pio_dev,
- struct kvm_vcpu *vcpu)
+static int pio_string_write(struct kvm_vcpu *vcpu)
{
struct kvm_pio_request *io = &vcpu->arch.pio;
void *pd = vcpu->arch.pio_data;
- int i;
+ int i, r = 0;
- mutex_lock(&vcpu->kvm->lock);
for (i = 0; i < io->cur_count; i++) {
- kvm_iodevice_write(pio_dev, io->port,
- io->size,
- pd);
+ if (kvm_io_bus_write(&vcpu->kvm->pio_bus,
+ io->port, io->size, pd)) {
+ r = -EOPNOTSUPP;
+ break;
+ }
pd += io->size;
}
- mutex_unlock(&vcpu->kvm->lock);
-}
-
-static struct kvm_io_device *vcpu_find_pio_dev(struct kvm_vcpu *vcpu,
- gpa_t addr, int len,
- int is_write)
-{
- return kvm_io_bus_find_dev(&vcpu->kvm->pio_bus, addr, len, is_write);
+ return r;
}
int kvm_emulate_pio(struct kvm_vcpu *vcpu, struct kvm_run *run, int in,
int size, unsigned port)
{
- struct kvm_io_device *pio_dev;
unsigned long val;
vcpu->run->exit_reason = KVM_EXIT_IO;
@@ -2630,19 +2983,13 @@ int kvm_emulate_pio(struct kvm_vcpu *vcpu, struct kvm_run *run, int in,
vcpu->arch.pio.down = 0;
vcpu->arch.pio.rep = 0;
- if (vcpu->run->io.direction == KVM_EXIT_IO_IN)
- KVMTRACE_2D(IO_READ, vcpu, vcpu->run->io.port, (u32)size,
- handler);
- else
- KVMTRACE_2D(IO_WRITE, vcpu, vcpu->run->io.port, (u32)size,
- handler);
+ trace_kvm_pio(vcpu->run->io.direction == KVM_EXIT_IO_OUT, port,
+ size, 1);
val = kvm_register_read(vcpu, VCPU_REGS_RAX);
memcpy(vcpu->arch.pio_data, &val, 4);
- pio_dev = vcpu_find_pio_dev(vcpu, port, size, !in);
- if (pio_dev) {
- kernel_pio(pio_dev, vcpu, vcpu->arch.pio_data);
+ if (!kernel_pio(vcpu, vcpu->arch.pio_data)) {
complete_pio(vcpu);
return 1;
}
@@ -2656,7 +3003,6 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, struct kvm_run *run, int in,
{
unsigned now, in_page;
int ret = 0;
- struct kvm_io_device *pio_dev;
vcpu->run->exit_reason = KVM_EXIT_IO;
vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT;
@@ -2669,12 +3015,8 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, struct kvm_run *run, int in,
vcpu->arch.pio.down = down;
vcpu->arch.pio.rep = rep;
- if (vcpu->run->io.direction == KVM_EXIT_IO_IN)
- KVMTRACE_2D(IO_READ, vcpu, vcpu->run->io.port, (u32)size,
- handler);
- else
- KVMTRACE_2D(IO_WRITE, vcpu, vcpu->run->io.port, (u32)size,
- handler);
+ trace_kvm_pio(vcpu->run->io.direction == KVM_EXIT_IO_OUT, port,
+ size, count);
if (!count) {
kvm_x86_ops->skip_emulated_instruction(vcpu);
@@ -2704,9 +3046,6 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, struct kvm_run *run, int in,
vcpu->arch.pio.guest_gva = address;
- pio_dev = vcpu_find_pio_dev(vcpu, port,
- vcpu->arch.pio.cur_count,
- !vcpu->arch.pio.in);
if (!vcpu->arch.pio.in) {
/* string PIO write */
ret = pio_copy_data(vcpu);
@@ -2714,16 +3053,13 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, struct kvm_run *run, int in,
kvm_inject_gp(vcpu, 0);
return 1;
}
- if (ret == 0 && pio_dev) {
- pio_string_write(pio_dev, vcpu);
+ if (ret == 0 && !pio_string_write(vcpu)) {
complete_pio(vcpu);
if (vcpu->arch.pio.count == 0)
ret = 1;
}
- } else if (pio_dev)
- pr_unimpl(vcpu, "no string pio read support yet, "
- "port %x size %d count %ld\n",
- port, size, count);
+ }
+ /* no string PIO read support yet */
return ret;
}
@@ -2756,10 +3092,7 @@ static int kvmclock_cpufreq_notifier(struct notifier_block *nb, unsigned long va
spin_lock(&kvm_lock);
list_for_each_entry(kvm, &vm_list, vm_list) {
- for (i = 0; i < KVM_MAX_VCPUS; ++i) {
- vcpu = kvm->vcpus[i];
- if (!vcpu)
- continue;
+ kvm_for_each_vcpu(i, vcpu, kvm) {
if (vcpu->cpu != freq->cpu)
continue;
if (!kvm_request_guest_time_update(vcpu))
@@ -2852,7 +3185,6 @@ void kvm_arch_exit(void)
int kvm_emulate_halt(struct kvm_vcpu *vcpu)
{
++vcpu->stat.halt_exits;
- KVMTRACE_0D(HLT, vcpu, handler);
if (irqchip_in_kernel(vcpu->kvm)) {
vcpu->arch.mp_state = KVM_MP_STATE_HALTED;
return 1;
@@ -2883,7 +3215,7 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
a2 = kvm_register_read(vcpu, VCPU_REGS_RDX);
a3 = kvm_register_read(vcpu, VCPU_REGS_RSI);
- KVMTRACE_1D(VMMCALL, vcpu, (u32)nr, handler);
+ trace_kvm_hypercall(nr, a0, a1, a2, a3);
if (!is_long_mode(vcpu)) {
nr &= 0xFFFFFFFF;
@@ -2893,6 +3225,11 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
a3 &= 0xFFFFFFFF;
}
+ if (kvm_x86_ops->get_cpl(vcpu) != 0) {
+ ret = -KVM_EPERM;
+ goto out;
+ }
+
switch (nr) {
case KVM_HC_VAPIC_POLL_IRQ:
ret = 0;
@@ -2904,6 +3241,7 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu)
ret = -KVM_ENOSYS;
break;
}
+out:
kvm_register_write(vcpu, VCPU_REGS_RAX, ret);
++vcpu->stat.hypercalls;
return r;
@@ -2983,8 +3321,6 @@ unsigned long realmode_get_cr(struct kvm_vcpu *vcpu, int cr)
vcpu_printf(vcpu, "%s: unexpected cr %u\n", __func__, cr);
return 0;
}
- KVMTRACE_3D(CR_READ, vcpu, (u32)cr, (u32)value,
- (u32)((u64)value >> 32), handler);
return value;
}
@@ -2992,9 +3328,6 @@ unsigned long realmode_get_cr(struct kvm_vcpu *vcpu, int cr)
void realmode_set_cr(struct kvm_vcpu *vcpu, int cr, unsigned long val,
unsigned long *rflags)
{
- KVMTRACE_3D(CR_WRITE, vcpu, (u32)cr, (u32)val,
- (u32)((u64)val >> 32), handler);
-
switch (cr) {
case 0:
kvm_set_cr0(vcpu, mk_cr_64(vcpu->arch.cr0, val));
@@ -3104,11 +3437,11 @@ void kvm_emulate_cpuid(struct kvm_vcpu *vcpu)
kvm_register_write(vcpu, VCPU_REGS_RDX, best->edx);
}
kvm_x86_ops->skip_emulated_instruction(vcpu);
- KVMTRACE_5D(CPUID, vcpu, function,
- (u32)kvm_register_read(vcpu, VCPU_REGS_RAX),
- (u32)kvm_register_read(vcpu, VCPU_REGS_RBX),
- (u32)kvm_register_read(vcpu, VCPU_REGS_RCX),
- (u32)kvm_register_read(vcpu, VCPU_REGS_RDX), handler);
+ trace_kvm_cpuid(function,
+ kvm_register_read(vcpu, VCPU_REGS_RAX),
+ kvm_register_read(vcpu, VCPU_REGS_RBX),
+ kvm_register_read(vcpu, VCPU_REGS_RCX),
+ kvm_register_read(vcpu, VCPU_REGS_RDX));
}
EXPORT_SYMBOL_GPL(kvm_emulate_cpuid);
@@ -3174,6 +3507,9 @@ static void update_cr8_intercept(struct kvm_vcpu *vcpu)
if (!kvm_x86_ops->update_cr8_intercept)
return;
+ if (!vcpu->arch.apic)
+ return;
+
if (!vcpu->arch.apic->vapic_addr)
max_irr = kvm_lapic_find_highest_irr(vcpu);
else
@@ -3187,12 +3523,16 @@ static void update_cr8_intercept(struct kvm_vcpu *vcpu)
kvm_x86_ops->update_cr8_intercept(vcpu, tpr, max_irr);
}
-static void inject_pending_irq(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+static void inject_pending_event(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
{
- if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
- kvm_x86_ops->set_interrupt_shadow(vcpu, 0);
-
/* try to reinject previous events if any */
+ if (vcpu->arch.exception.pending) {
+ kvm_x86_ops->queue_exception(vcpu, vcpu->arch.exception.nr,
+ vcpu->arch.exception.has_error_code,
+ vcpu->arch.exception.error_code);
+ return;
+ }
+
if (vcpu->arch.nmi_injected) {
kvm_x86_ops->set_nmi(vcpu);
return;
@@ -3266,16 +3606,14 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
smp_mb__after_clear_bit();
if (vcpu->requests || need_resched() || signal_pending(current)) {
+ set_bit(KVM_REQ_KICK, &vcpu->requests);
local_irq_enable();
preempt_enable();
r = 1;
goto out;
}
- if (vcpu->arch.exception.pending)
- __queue_exception(vcpu);
- else
- inject_pending_irq(vcpu, kvm_run);
+ inject_pending_event(vcpu, kvm_run);
/* enable NMI/IRQ window open exits if needed */
if (vcpu->arch.nmi_pending)
@@ -3292,14 +3630,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
kvm_guest_enter();
- get_debugreg(vcpu->arch.host_dr6, 6);
- get_debugreg(vcpu->arch.host_dr7, 7);
if (unlikely(vcpu->arch.switch_db_regs)) {
- get_debugreg(vcpu->arch.host_db[0], 0);
- get_debugreg(vcpu->arch.host_db[1], 1);
- get_debugreg(vcpu->arch.host_db[2], 2);
- get_debugreg(vcpu->arch.host_db[3], 3);
-
set_debugreg(0, 7);
set_debugreg(vcpu->arch.eff_db[0], 0);
set_debugreg(vcpu->arch.eff_db[1], 1);
@@ -3307,18 +3638,17 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
set_debugreg(vcpu->arch.eff_db[3], 3);
}
- KVMTRACE_0D(VMENTRY, vcpu, entryexit);
+ trace_kvm_entry(vcpu->vcpu_id);
kvm_x86_ops->run(vcpu, kvm_run);
- if (unlikely(vcpu->arch.switch_db_regs)) {
- set_debugreg(0, 7);
- set_debugreg(vcpu->arch.host_db[0], 0);
- set_debugreg(vcpu->arch.host_db[1], 1);
- set_debugreg(vcpu->arch.host_db[2], 2);
- set_debugreg(vcpu->arch.host_db[3], 3);
+ if (unlikely(vcpu->arch.switch_db_regs || test_thread_flag(TIF_DEBUG))) {
+ set_debugreg(current->thread.debugreg0, 0);
+ set_debugreg(current->thread.debugreg1, 1);
+ set_debugreg(current->thread.debugreg2, 2);
+ set_debugreg(current->thread.debugreg3, 3);
+ set_debugreg(current->thread.debugreg6, 6);
+ set_debugreg(current->thread.debugreg7, 7);
}
- set_debugreg(vcpu->arch.host_dr6, 6);
- set_debugreg(vcpu->arch.host_dr7, 7);
set_bit(KVM_REQ_KICK, &vcpu->requests);
local_irq_enable();
@@ -3648,11 +3978,8 @@ static void kvm_set_segment(struct kvm_vcpu *vcpu,
static void seg_desct_to_kvm_desct(struct desc_struct *seg_desc, u16 selector,
struct kvm_segment *kvm_desct)
{
- kvm_desct->base = seg_desc->base0;
- kvm_desct->base |= seg_desc->base1 << 16;
- kvm_desct->base |= seg_desc->base2 << 24;
- kvm_desct->limit = seg_desc->limit0;
- kvm_desct->limit |= seg_desc->limit << 16;
+ kvm_desct->base = get_desc_base(seg_desc);
+ kvm_desct->limit = get_desc_limit(seg_desc);
if (seg_desc->g) {
kvm_desct->limit <<= 12;
kvm_desct->limit |= 0xfff;
@@ -3696,7 +4023,6 @@ static void get_segment_descriptor_dtable(struct kvm_vcpu *vcpu,
static int load_guest_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
struct desc_struct *seg_desc)
{
- gpa_t gpa;
struct descriptor_table dtable;
u16 index = selector >> 3;
@@ -3706,16 +4032,13 @@ static int load_guest_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
kvm_queue_exception_e(vcpu, GP_VECTOR, selector & 0xfffc);
return 1;
}
- gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, dtable.base);
- gpa += index * 8;
- return kvm_read_guest(vcpu->kvm, gpa, seg_desc, 8);
+ return kvm_read_guest_virt(dtable.base + index*8, seg_desc, sizeof(*seg_desc), vcpu);
}
/* allowed just for 8 bytes segments */
static int save_guest_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
struct desc_struct *seg_desc)
{
- gpa_t gpa;
struct descriptor_table dtable;
u16 index = selector >> 3;
@@ -3723,19 +4046,13 @@ static int save_guest_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
if (dtable.limit < index * 8 + 7)
return 1;
- gpa = vcpu->arch.mmu.gva_to_gpa(vcpu, dtable.base);
- gpa += index * 8;
- return kvm_write_guest(vcpu->kvm, gpa, seg_desc, 8);
+ return kvm_write_guest_virt(dtable.base + index*8, seg_desc, sizeof(*seg_desc), vcpu);
}
static u32 get_tss_base_addr(struct kvm_vcpu *vcpu,
struct desc_struct *seg_desc)
{
- u32 base_addr;
-
- base_addr = seg_desc->base0;
- base_addr |= (seg_desc->base1 << 16);
- base_addr |= (seg_desc->base2 << 24);
+ u32 base_addr = get_desc_base(seg_desc);
return vcpu->arch.mmu.gva_to_gpa(vcpu, base_addr);
}
@@ -3780,12 +4097,19 @@ static int kvm_load_realmode_segment(struct kvm_vcpu *vcpu, u16 selector, int se
return 0;
}
+static int is_vm86_segment(struct kvm_vcpu *vcpu, int seg)
+{
+ return (seg != VCPU_SREG_LDTR) &&
+ (seg != VCPU_SREG_TR) &&
+ (kvm_x86_ops->get_rflags(vcpu) & X86_EFLAGS_VM);
+}
+
int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
int type_bits, int seg)
{
struct kvm_segment kvm_seg;
- if (!(vcpu->arch.cr0 & X86_CR0_PE))
+ if (is_vm86_segment(vcpu, seg) || !(vcpu->arch.cr0 & X86_CR0_PE))
return kvm_load_realmode_segment(vcpu, selector, seg);
if (load_segment_descriptor_to_kvm_desct(vcpu, selector, &kvm_seg))
return 1;
@@ -4024,7 +4348,7 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)
}
}
- if (!nseg_desc.p || (nseg_desc.limit0 | nseg_desc.limit << 16) < 0x67) {
+ if (!nseg_desc.p || get_desc_limit(&nseg_desc) < 0x67) {
kvm_queue_exception_e(vcpu, TS_VECTOR, tss_selector & 0xfffc);
return 1;
}
@@ -4094,13 +4418,7 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
vcpu->arch.cr2 = sregs->cr2;
mmu_reset_needed |= vcpu->arch.cr3 != sregs->cr3;
-
- down_read(&vcpu->kvm->slots_lock);
- if (gfn_to_memslot(vcpu->kvm, sregs->cr3 >> PAGE_SHIFT))
- vcpu->arch.cr3 = sregs->cr3;
- else
- set_bit(KVM_REQ_TRIPLE_FAULT, &vcpu->requests);
- up_read(&vcpu->kvm->slots_lock);
+ vcpu->arch.cr3 = sregs->cr3;
kvm_set_cr8(vcpu, sregs->cr8);
@@ -4142,8 +4460,10 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
kvm_set_segment(vcpu, &sregs->tr, VCPU_SREG_TR);
kvm_set_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR);
+ update_cr8_intercept(vcpu);
+
/* Older userspace won't unhalt the vcpu on reset. */
- if (vcpu->vcpu_id == 0 && kvm_rip_read(vcpu) == 0xfff0 &&
+ if (kvm_vcpu_is_bsp(vcpu) && kvm_rip_read(vcpu) == 0xfff0 &&
sregs->cs.selector == 0xf000 && sregs->cs.base == 0xffff0000 &&
!(vcpu->arch.cr0 & X86_CR0_PE))
vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
@@ -4414,7 +4734,7 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
kvm = vcpu->kvm;
vcpu->arch.mmu.root_hpa = INVALID_PAGE;
- if (!irqchip_in_kernel(kvm) || vcpu->vcpu_id == 0)
+ if (!irqchip_in_kernel(kvm) || kvm_vcpu_is_bsp(vcpu))
vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
else
vcpu->arch.mp_state = KVM_MP_STATE_UNINITIALIZED;
@@ -4436,6 +4756,14 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
goto fail_mmu_destroy;
}
+ vcpu->arch.mce_banks = kzalloc(KVM_MAX_MCE_BANKS * sizeof(u64) * 4,
+ GFP_KERNEL);
+ if (!vcpu->arch.mce_banks) {
+ r = -ENOMEM;
+ goto fail_mmu_destroy;
+ }
+ vcpu->arch.mcg_cap = KVM_MAX_MCE_BANKS;
+
return 0;
fail_mmu_destroy:
@@ -4483,20 +4811,22 @@ static void kvm_unload_vcpu_mmu(struct kvm_vcpu *vcpu)
static void kvm_free_vcpus(struct kvm *kvm)
{
unsigned int i;
+ struct kvm_vcpu *vcpu;
/*
* Unpin any mmu pages first.
*/
- for (i = 0; i < KVM_MAX_VCPUS; ++i)
- if (kvm->vcpus[i])
- kvm_unload_vcpu_mmu(kvm->vcpus[i]);
- for (i = 0; i < KVM_MAX_VCPUS; ++i) {
- if (kvm->vcpus[i]) {
- kvm_arch_vcpu_free(kvm->vcpus[i]);
- kvm->vcpus[i] = NULL;
- }
- }
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ kvm_unload_vcpu_mmu(vcpu);
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ kvm_arch_vcpu_free(vcpu);
+
+ mutex_lock(&kvm->lock);
+ for (i = 0; i < atomic_read(&kvm->online_vcpus); i++)
+ kvm->vcpus[i] = NULL;
+ atomic_set(&kvm->online_vcpus, 0);
+ mutex_unlock(&kvm->lock);
}
void kvm_arch_sync_events(struct kvm *kvm)
@@ -4573,7 +4903,6 @@ int kvm_arch_set_memory_region(struct kvm *kvm,
kvm_mmu_slot_remove_write_access(kvm, mem->slot);
spin_unlock(&kvm->mmu_lock);
- kvm_flush_remote_tlbs(kvm);
return 0;
}
@@ -4587,8 +4916,10 @@ void kvm_arch_flush_shadow(struct kvm *kvm)
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
{
return vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE
- || vcpu->arch.mp_state == KVM_MP_STATE_SIPI_RECEIVED
- || vcpu->arch.nmi_pending;
+ || vcpu->arch.mp_state == KVM_MP_STATE_SIPI_RECEIVED
+ || vcpu->arch.nmi_pending ||
+ (kvm_arch_interrupt_allowed(vcpu) &&
+ kvm_cpu_has_interrupt(vcpu));
}
void kvm_vcpu_kick(struct kvm_vcpu *vcpu)
@@ -4612,3 +4943,9 @@ int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu)
{
return kvm_x86_ops->interrupt_allowed(vcpu);
}
+
+EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_exit);
+EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_inj_virq);
+EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_page_fault);
+EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_msr);
+EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_cr);
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index 4c8e10af78e..5eadea585d2 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -31,4 +31,8 @@ static inline bool kvm_exception_is_soft(unsigned int nr)
{
return (nr == BP_VECTOR) || (nr == OF_VECTOR);
}
+
+struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,
+ u32 function, u32 index);
+
#endif
diff --git a/arch/x86/mm/highmem_32.c b/arch/x86/mm/highmem_32.c
index 1617958a380..63a6ba66cbe 100644
--- a/arch/x86/mm/highmem_32.c
+++ b/arch/x86/mm/highmem_32.c
@@ -104,6 +104,7 @@ EXPORT_SYMBOL(kunmap);
EXPORT_SYMBOL(kmap_atomic);
EXPORT_SYMBOL(kunmap_atomic);
EXPORT_SYMBOL(kmap_atomic_prot);
+EXPORT_SYMBOL(kmap_atomic_to_page);
void __init set_highmem_pages_init(void)
{
diff --git a/block/Makefile b/block/Makefile
index 6c54ed0ff75..ba74ca6bfa1 100644
--- a/block/Makefile
+++ b/block/Makefile
@@ -5,7 +5,7 @@
obj-$(CONFIG_BLOCK) := elevator.o blk-core.o blk-tag.o blk-sysfs.o \
blk-barrier.o blk-settings.o blk-ioc.o blk-map.o \
blk-exec.o blk-merge.o blk-softirq.o blk-timeout.o \
- ioctl.o genhd.o scsi_ioctl.o
+ blk-iopoll.o ioctl.o genhd.o scsi_ioctl.o
obj-$(CONFIG_BLK_DEV_BSG) += bsg.o
obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o
diff --git a/block/blk-barrier.c b/block/blk-barrier.c
index 30022b4e2f6..6593ab39cfe 100644
--- a/block/blk-barrier.c
+++ b/block/blk-barrier.c
@@ -348,6 +348,9 @@ static void blkdev_discard_end_io(struct bio *bio, int err)
clear_bit(BIO_UPTODATE, &bio->bi_flags);
}
+ if (bio->bi_private)
+ complete(bio->bi_private);
+
bio_put(bio);
}
@@ -357,21 +360,20 @@ static void blkdev_discard_end_io(struct bio *bio, int err)
* @sector: start sector
* @nr_sects: number of sectors to discard
* @gfp_mask: memory allocation flags (for bio_alloc)
+ * @flags: DISCARD_FL_* flags to control behaviour
*
* Description:
- * Issue a discard request for the sectors in question. Does not wait.
+ * Issue a discard request for the sectors in question.
*/
-int blkdev_issue_discard(struct block_device *bdev,
- sector_t sector, sector_t nr_sects, gfp_t gfp_mask)
+int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
+ sector_t nr_sects, gfp_t gfp_mask, int flags)
{
- struct request_queue *q;
- struct bio *bio;
+ DECLARE_COMPLETION_ONSTACK(wait);
+ struct request_queue *q = bdev_get_queue(bdev);
+ int type = flags & DISCARD_FL_BARRIER ?
+ DISCARD_BARRIER : DISCARD_NOBARRIER;
int ret = 0;
- if (bdev->bd_disk == NULL)
- return -ENXIO;
-
- q = bdev_get_queue(bdev);
if (!q)
return -ENXIO;
@@ -379,12 +381,14 @@ int blkdev_issue_discard(struct block_device *bdev,
return -EOPNOTSUPP;
while (nr_sects && !ret) {
- bio = bio_alloc(gfp_mask, 0);
+ struct bio *bio = bio_alloc(gfp_mask, 0);
if (!bio)
return -ENOMEM;
bio->bi_end_io = blkdev_discard_end_io;
bio->bi_bdev = bdev;
+ if (flags & DISCARD_FL_WAIT)
+ bio->bi_private = &wait;
bio->bi_sector = sector;
@@ -396,10 +400,13 @@ int blkdev_issue_discard(struct block_device *bdev,
bio->bi_size = nr_sects << 9;
nr_sects = 0;
}
+
bio_get(bio);
- submit_bio(DISCARD_BARRIER, bio);
+ submit_bio(type, bio);
+
+ if (flags & DISCARD_FL_WAIT)
+ wait_for_completion(&wait);
- /* Check if it failed immediately */
if (bio_flagged(bio, BIO_EOPNOTSUPP))
ret = -EOPNOTSUPP;
else if (!bio_flagged(bio, BIO_UPTODATE))
diff --git a/block/blk-core.c b/block/blk-core.c
index e695634882a..8135228e4b2 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -69,7 +69,7 @@ static void drive_stat_acct(struct request *rq, int new_io)
part_stat_inc(cpu, part, merges[rw]);
else {
part_round_stats(cpu, part);
- part_inc_in_flight(part);
+ part_inc_in_flight(part, rw);
}
part_stat_unlock();
@@ -1031,7 +1031,7 @@ static void part_round_stats_single(int cpu, struct hd_struct *part,
if (part->in_flight) {
__part_stat_add(cpu, part, time_in_queue,
- part->in_flight * (now - part->stamp));
+ part_in_flight(part) * (now - part->stamp));
__part_stat_add(cpu, part, io_ticks, (now - part->stamp));
}
part->stamp = now;
@@ -1112,31 +1112,27 @@ void init_request_from_bio(struct request *req, struct bio *bio)
req->cmd_type = REQ_TYPE_FS;
/*
- * inherit FAILFAST from bio (for read-ahead, and explicit FAILFAST)
+ * Inherit FAILFAST from bio (for read-ahead, and explicit
+ * FAILFAST). FAILFAST flags are identical for req and bio.
*/
- if (bio_rw_ahead(bio))
- req->cmd_flags |= (REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |
- REQ_FAILFAST_DRIVER);
- if (bio_failfast_dev(bio))
- req->cmd_flags |= REQ_FAILFAST_DEV;
- if (bio_failfast_transport(bio))
- req->cmd_flags |= REQ_FAILFAST_TRANSPORT;
- if (bio_failfast_driver(bio))
- req->cmd_flags |= REQ_FAILFAST_DRIVER;
-
- if (unlikely(bio_discard(bio))) {
+ if (bio_rw_flagged(bio, BIO_RW_AHEAD))
+ req->cmd_flags |= REQ_FAILFAST_MASK;
+ else
+ req->cmd_flags |= bio->bi_rw & REQ_FAILFAST_MASK;
+
+ if (unlikely(bio_rw_flagged(bio, BIO_RW_DISCARD))) {
req->cmd_flags |= REQ_DISCARD;
- if (bio_barrier(bio))
+ if (bio_rw_flagged(bio, BIO_RW_BARRIER))
req->cmd_flags |= REQ_SOFTBARRIER;
req->q->prepare_discard_fn(req->q, req);
- } else if (unlikely(bio_barrier(bio)))
+ } else if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER)))
req->cmd_flags |= REQ_HARDBARRIER;
- if (bio_sync(bio))
+ if (bio_rw_flagged(bio, BIO_RW_SYNCIO))
req->cmd_flags |= REQ_RW_SYNC;
- if (bio_rw_meta(bio))
+ if (bio_rw_flagged(bio, BIO_RW_META))
req->cmd_flags |= REQ_RW_META;
- if (bio_noidle(bio))
+ if (bio_rw_flagged(bio, BIO_RW_NOIDLE))
req->cmd_flags |= REQ_NOIDLE;
req->errors = 0;
@@ -1151,7 +1147,7 @@ void init_request_from_bio(struct request *req, struct bio *bio)
*/
static inline bool queue_should_plug(struct request_queue *q)
{
- return !(blk_queue_nonrot(q) && blk_queue_tagged(q));
+ return !(blk_queue_nonrot(q) && blk_queue_queuing(q));
}
static int __make_request(struct request_queue *q, struct bio *bio)
@@ -1160,11 +1156,12 @@ static int __make_request(struct request_queue *q, struct bio *bio)
int el_ret;
unsigned int bytes = bio->bi_size;
const unsigned short prio = bio_prio(bio);
- const int sync = bio_sync(bio);
- const int unplug = bio_unplug(bio);
+ const bool sync = bio_rw_flagged(bio, BIO_RW_SYNCIO);
+ const bool unplug = bio_rw_flagged(bio, BIO_RW_UNPLUG);
+ const unsigned int ff = bio->bi_rw & REQ_FAILFAST_MASK;
int rw_flags;
- if (bio_barrier(bio) && bio_has_data(bio) &&
+ if (bio_rw_flagged(bio, BIO_RW_BARRIER) && bio_has_data(bio) &&
(q->next_ordered == QUEUE_ORDERED_NONE)) {
bio_endio(bio, -EOPNOTSUPP);
return 0;
@@ -1178,7 +1175,7 @@ static int __make_request(struct request_queue *q, struct bio *bio)
spin_lock_irq(q->queue_lock);
- if (unlikely(bio_barrier(bio)) || elv_queue_empty(q))
+ if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER)) || elv_queue_empty(q))
goto get_rq;
el_ret = elv_merge(q, &req, bio);
@@ -1191,6 +1188,9 @@ static int __make_request(struct request_queue *q, struct bio *bio)
trace_block_bio_backmerge(q, bio);
+ if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff)
+ blk_rq_set_mixed_merge(req);
+
req->biotail->bi_next = bio;
req->biotail = bio;
req->__data_len += bytes;
@@ -1210,6 +1210,12 @@ static int __make_request(struct request_queue *q, struct bio *bio)
trace_block_bio_frontmerge(q, bio);
+ if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff) {
+ blk_rq_set_mixed_merge(req);
+ req->cmd_flags &= ~REQ_FAILFAST_MASK;
+ req->cmd_flags |= ff;
+ }
+
bio->bi_next = req->bio;
req->bio = bio;
@@ -1457,19 +1463,20 @@ static inline void __generic_make_request(struct bio *bio)
if (old_sector != -1)
trace_block_remap(q, bio, old_dev, old_sector);
- trace_block_bio_queue(q, bio);
-
old_sector = bio->bi_sector;
old_dev = bio->bi_bdev->bd_dev;
if (bio_check_eod(bio, nr_sectors))
goto end_io;
- if (bio_discard(bio) && !q->prepare_discard_fn) {
+ if (bio_rw_flagged(bio, BIO_RW_DISCARD) &&
+ !q->prepare_discard_fn) {
err = -EOPNOTSUPP;
goto end_io;
}
+ trace_block_bio_queue(q, bio);
+
ret = q->make_request_fn(q, bio);
} while (ret);
@@ -1654,6 +1661,50 @@ int blk_insert_cloned_request(struct request_queue *q, struct request *rq)
}
EXPORT_SYMBOL_GPL(blk_insert_cloned_request);
+/**
+ * blk_rq_err_bytes - determine number of bytes till the next failure boundary
+ * @rq: request to examine
+ *
+ * Description:
+ * A request could be merge of IOs which require different failure
+ * handling. This function determines the number of bytes which
+ * can be failed from the beginning of the request without
+ * crossing into area which need to be retried further.
+ *
+ * Return:
+ * The number of bytes to fail.
+ *
+ * Context:
+ * queue_lock must be held.
+ */
+unsigned int blk_rq_err_bytes(const struct request *rq)
+{
+ unsigned int ff = rq->cmd_flags & REQ_FAILFAST_MASK;
+ unsigned int bytes = 0;
+ struct bio *bio;
+
+ if (!(rq->cmd_flags & REQ_MIXED_MERGE))
+ return blk_rq_bytes(rq);
+
+ /*
+ * Currently the only 'mixing' which can happen is between
+ * different fastfail types. We can safely fail portions
+ * which have all the failfast bits that the first one has -
+ * the ones which are at least as eager to fail as the first
+ * one.
+ */
+ for (bio = rq->bio; bio; bio = bio->bi_next) {
+ if ((bio->bi_rw & ff) != ff)
+ break;
+ bytes += bio->bi_size;
+ }
+
+ /* this could lead to infinite loop */
+ BUG_ON(blk_rq_bytes(rq) && !bytes);
+ return bytes;
+}
+EXPORT_SYMBOL_GPL(blk_rq_err_bytes);
+
static void blk_account_io_completion(struct request *req, unsigned int bytes)
{
if (blk_do_io_stat(req)) {
@@ -1687,7 +1738,7 @@ static void blk_account_io_done(struct request *req)
part_stat_inc(cpu, part, ios[rw]);
part_stat_add(cpu, part, ticks[rw], duration);
part_round_stats(cpu, part);
- part_dec_in_flight(part);
+ part_dec_in_flight(part, rw);
part_stat_unlock();
}
@@ -1807,8 +1858,15 @@ void blk_dequeue_request(struct request *rq)
* and to it is freed is accounted as io that is in progress at
* the driver side.
*/
- if (blk_account_rq(rq))
+ if (blk_account_rq(rq)) {
q->in_flight[rq_is_sync(rq)]++;
+ /*
+ * Mark this device as supporting hardware queuing, if
+ * we have more IOs in flight than 4.
+ */
+ if (!blk_queue_queuing(q) && queue_in_flight(q) > 4)
+ set_bit(QUEUE_FLAG_CQ, &q->queue_flags);
+ }
}
/**
@@ -2000,6 +2058,12 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes)
if (blk_fs_request(req) || blk_discard_rq(req))
req->__sector += total_bytes >> 9;
+ /* mixed attributes always follow the first bio */
+ if (req->cmd_flags & REQ_MIXED_MERGE) {
+ req->cmd_flags &= ~REQ_FAILFAST_MASK;
+ req->cmd_flags |= req->bio->bi_rw & REQ_FAILFAST_MASK;
+ }
+
/*
* If total number of sectors is less than the first segment
* size, something has gone terribly wrong.
@@ -2179,6 +2243,25 @@ bool blk_end_request_cur(struct request *rq, int error)
EXPORT_SYMBOL(blk_end_request_cur);
/**
+ * blk_end_request_err - Finish a request till the next failure boundary.
+ * @rq: the request to finish till the next failure boundary for
+ * @error: must be negative errno
+ *
+ * Description:
+ * Complete @rq till the next failure boundary.
+ *
+ * Return:
+ * %false - we are done with this request
+ * %true - still buffers pending for this request
+ */
+bool blk_end_request_err(struct request *rq, int error)
+{
+ WARN_ON(error >= 0);
+ return blk_end_request(rq, error, blk_rq_err_bytes(rq));
+}
+EXPORT_SYMBOL_GPL(blk_end_request_err);
+
+/**
* __blk_end_request - Helper function for drivers to complete the request.
* @rq: the request being processed
* @error: %0 for success, < %0 for error
@@ -2237,12 +2320,31 @@ bool __blk_end_request_cur(struct request *rq, int error)
}
EXPORT_SYMBOL(__blk_end_request_cur);
+/**
+ * __blk_end_request_err - Finish a request till the next failure boundary.
+ * @rq: the request to finish till the next failure boundary for
+ * @error: must be negative errno
+ *
+ * Description:
+ * Complete @rq till the next failure boundary. Must be called
+ * with queue lock held.
+ *
+ * Return:
+ * %false - we are done with this request
+ * %true - still buffers pending for this request
+ */
+bool __blk_end_request_err(struct request *rq, int error)
+{
+ WARN_ON(error >= 0);
+ return __blk_end_request(rq, error, blk_rq_err_bytes(rq));
+}
+EXPORT_SYMBOL_GPL(__blk_end_request_err);
+
void blk_rq_bio_prep(struct request_queue *q, struct request *rq,
struct bio *bio)
{
- /* Bit 0 (R/W) is identical in rq->cmd_flags and bio->bi_rw, and
- we want BIO_RW_AHEAD (bit 1) to imply REQ_FAILFAST (bit 1). */
- rq->cmd_flags |= (bio->bi_rw & 3);
+ /* Bit 0 (R/W) is identical in rq->cmd_flags and bio->bi_rw */
+ rq->cmd_flags |= bio->bi_rw & REQ_RW;
if (bio_has_data(bio)) {
rq->nr_phys_segments = bio_phys_segments(q, bio);
diff --git a/block/blk-iopoll.c b/block/blk-iopoll.c
new file mode 100644
index 00000000000..ca564202ed7
--- /dev/null
+++ b/block/blk-iopoll.c
@@ -0,0 +1,227 @@
+/*
+ * Functions related to interrupt-poll handling in the block layer. This
+ * is similar to NAPI for network devices.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/bio.h>
+#include <linux/blkdev.h>
+#include <linux/interrupt.h>
+#include <linux/cpu.h>
+#include <linux/blk-iopoll.h>
+#include <linux/delay.h>
+
+#include "blk.h"
+
+int blk_iopoll_enabled = 1;
+EXPORT_SYMBOL(blk_iopoll_enabled);
+
+static unsigned int blk_iopoll_budget __read_mostly = 256;
+
+static DEFINE_PER_CPU(struct list_head, blk_cpu_iopoll);
+
+/**
+ * blk_iopoll_sched - Schedule a run of the iopoll handler
+ * @iop: The parent iopoll structure
+ *
+ * Description:
+ * Add this blk_iopoll structure to the pending poll list and trigger the
+ * raise of the blk iopoll softirq. The driver must already have gotten a
+ * succesful return from blk_iopoll_sched_prep() before calling this.
+ **/
+void blk_iopoll_sched(struct blk_iopoll *iop)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ list_add_tail(&iop->list, &__get_cpu_var(blk_cpu_iopoll));
+ __raise_softirq_irqoff(BLOCK_IOPOLL_SOFTIRQ);
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL(blk_iopoll_sched);
+
+/**
+ * __blk_iopoll_complete - Mark this @iop as un-polled again
+ * @iop: The parent iopoll structure
+ *
+ * Description:
+ * See blk_iopoll_complete(). This function must be called with interrupts
+ * disabled.
+ **/
+void __blk_iopoll_complete(struct blk_iopoll *iop)
+{
+ list_del(&iop->list);
+ smp_mb__before_clear_bit();
+ clear_bit_unlock(IOPOLL_F_SCHED, &iop->state);
+}
+EXPORT_SYMBOL(__blk_iopoll_complete);
+
+/**
+ * blk_iopoll_complete - Mark this @iop as un-polled again
+ * @iop: The parent iopoll structure
+ *
+ * Description:
+ * If a driver consumes less than the assigned budget in its run of the
+ * iopoll handler, it'll end the polled mode by calling this function. The
+ * iopoll handler will not be invoked again before blk_iopoll_sched_prep()
+ * is called.
+ **/
+void blk_iopoll_complete(struct blk_iopoll *iopoll)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ __blk_iopoll_complete(iopoll);
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL(blk_iopoll_complete);
+
+static void blk_iopoll_softirq(struct softirq_action *h)
+{
+ struct list_head *list = &__get_cpu_var(blk_cpu_iopoll);
+ int rearm = 0, budget = blk_iopoll_budget;
+ unsigned long start_time = jiffies;
+
+ local_irq_disable();
+
+ while (!list_empty(list)) {
+ struct blk_iopoll *iop;
+ int work, weight;
+
+ /*
+ * If softirq window is exhausted then punt.
+ */
+ if (budget <= 0 || time_after(jiffies, start_time)) {
+ rearm = 1;
+ break;
+ }
+
+ local_irq_enable();
+
+ /* Even though interrupts have been re-enabled, this
+ * access is safe because interrupts can only add new
+ * entries to the tail of this list, and only ->poll()
+ * calls can remove this head entry from the list.
+ */
+ iop = list_entry(list->next, struct blk_iopoll, list);
+
+ weight = iop->weight;
+ work = 0;
+ if (test_bit(IOPOLL_F_SCHED, &iop->state))
+ work = iop->poll(iop, weight);
+
+ budget -= work;
+
+ local_irq_disable();
+
+ /*
+ * Drivers must not modify the iopoll state, if they
+ * consume their assigned weight (or more, some drivers can't
+ * easily just stop processing, they have to complete an
+ * entire mask of commands).In such cases this code
+ * still "owns" the iopoll instance and therefore can
+ * move the instance around on the list at-will.
+ */
+ if (work >= weight) {
+ if (blk_iopoll_disable_pending(iop))
+ __blk_iopoll_complete(iop);
+ else
+ list_move_tail(&iop->list, list);
+ }
+ }
+
+ if (rearm)
+ __raise_softirq_irqoff(BLOCK_IOPOLL_SOFTIRQ);
+
+ local_irq_enable();
+}
+
+/**
+ * blk_iopoll_disable - Disable iopoll on this @iop
+ * @iop: The parent iopoll structure
+ *
+ * Description:
+ * Disable io polling and wait for any pending callbacks to have completed.
+ **/
+void blk_iopoll_disable(struct blk_iopoll *iop)
+{
+ set_bit(IOPOLL_F_DISABLE, &iop->state);
+ while (test_and_set_bit(IOPOLL_F_SCHED, &iop->state))
+ msleep(1);
+ clear_bit(IOPOLL_F_DISABLE, &iop->state);
+}
+EXPORT_SYMBOL(blk_iopoll_disable);
+
+/**
+ * blk_iopoll_enable - Enable iopoll on this @iop
+ * @iop: The parent iopoll structure
+ *
+ * Description:
+ * Enable iopoll on this @iop. Note that the handler run will not be
+ * scheduled, it will only mark it as active.
+ **/
+void blk_iopoll_enable(struct blk_iopoll *iop)
+{
+ BUG_ON(!test_bit(IOPOLL_F_SCHED, &iop->state));
+ smp_mb__before_clear_bit();
+ clear_bit_unlock(IOPOLL_F_SCHED, &iop->state);
+}
+EXPORT_SYMBOL(blk_iopoll_enable);
+
+/**
+ * blk_iopoll_init - Initialize this @iop
+ * @iop: The parent iopoll structure
+ * @weight: The default weight (or command completion budget)
+ * @poll_fn: The handler to invoke
+ *
+ * Description:
+ * Initialize this blk_iopoll structure. Before being actively used, the
+ * driver must call blk_iopoll_enable().
+ **/
+void blk_iopoll_init(struct blk_iopoll *iop, int weight, blk_iopoll_fn *poll_fn)
+{
+ memset(iop, 0, sizeof(*iop));
+ INIT_LIST_HEAD(&iop->list);
+ iop->weight = weight;
+ iop->poll = poll_fn;
+ set_bit(IOPOLL_F_SCHED, &iop->state);
+}
+EXPORT_SYMBOL(blk_iopoll_init);
+
+static int __cpuinit blk_iopoll_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ /*
+ * If a CPU goes away, splice its entries to the current CPU
+ * and trigger a run of the softirq
+ */
+ if (action == CPU_DEAD || action == CPU_DEAD_FROZEN) {
+ int cpu = (unsigned long) hcpu;
+
+ local_irq_disable();
+ list_splice_init(&per_cpu(blk_cpu_iopoll, cpu),
+ &__get_cpu_var(blk_cpu_iopoll));
+ __raise_softirq_irqoff(BLOCK_IOPOLL_SOFTIRQ);
+ local_irq_enable();
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata blk_iopoll_cpu_notifier = {
+ .notifier_call = blk_iopoll_cpu_notify,
+};
+
+static __init int blk_iopoll_setup(void)
+{
+ int i;
+
+ for_each_possible_cpu(i)
+ INIT_LIST_HEAD(&per_cpu(blk_cpu_iopoll, i));
+
+ open_softirq(BLOCK_IOPOLL_SOFTIRQ, blk_iopoll_softirq);
+ register_hotcpu_notifier(&blk_iopoll_cpu_notifier);
+ return 0;
+}
+subsys_initcall(blk_iopoll_setup);
diff --git a/block/blk-merge.c b/block/blk-merge.c
index e1999679a4d..99cb5cf1f44 100644
--- a/block/blk-merge.c
+++ b/block/blk-merge.c
@@ -311,6 +311,36 @@ static int ll_merge_requests_fn(struct request_queue *q, struct request *req,
return 1;
}
+/**
+ * blk_rq_set_mixed_merge - mark a request as mixed merge
+ * @rq: request to mark as mixed merge
+ *
+ * Description:
+ * @rq is about to be mixed merged. Make sure the attributes
+ * which can be mixed are set in each bio and mark @rq as mixed
+ * merged.
+ */
+void blk_rq_set_mixed_merge(struct request *rq)
+{
+ unsigned int ff = rq->cmd_flags & REQ_FAILFAST_MASK;
+ struct bio *bio;
+
+ if (rq->cmd_flags & REQ_MIXED_MERGE)
+ return;
+
+ /*
+ * @rq will no longer represent mixable attributes for all the
+ * contained bios. It will just track those of the first one.
+ * Distributes the attributs to each bio.
+ */
+ for (bio = rq->bio; bio; bio = bio->bi_next) {
+ WARN_ON_ONCE((bio->bi_rw & REQ_FAILFAST_MASK) &&
+ (bio->bi_rw & REQ_FAILFAST_MASK) != ff);
+ bio->bi_rw |= ff;
+ }
+ rq->cmd_flags |= REQ_MIXED_MERGE;
+}
+
static void blk_account_io_merge(struct request *req)
{
if (blk_do_io_stat(req)) {
@@ -321,7 +351,7 @@ static void blk_account_io_merge(struct request *req)
part = disk_map_sector_rcu(req->rq_disk, blk_rq_pos(req));
part_round_stats(cpu, part);
- part_dec_in_flight(part);
+ part_dec_in_flight(part, rq_data_dir(req));
part_stat_unlock();
}
@@ -350,12 +380,6 @@ static int attempt_merge(struct request_queue *q, struct request *req,
if (blk_integrity_rq(req) != blk_integrity_rq(next))
return 0;
- /* don't merge requests of different failfast settings */
- if (blk_failfast_dev(req) != blk_failfast_dev(next) ||
- blk_failfast_transport(req) != blk_failfast_transport(next) ||
- blk_failfast_driver(req) != blk_failfast_driver(next))
- return 0;
-
/*
* If we are allowed to merge, then append bio list
* from next to rq and release next. merge_requests_fn
@@ -366,6 +390,19 @@ static int attempt_merge(struct request_queue *q, struct request *req,
return 0;
/*
+ * If failfast settings disagree or any of the two is already
+ * a mixed merge, mark both as mixed before proceeding. This
+ * makes sure that all involved bios have mixable attributes
+ * set properly.
+ */
+ if ((req->cmd_flags | next->cmd_flags) & REQ_MIXED_MERGE ||
+ (req->cmd_flags & REQ_FAILFAST_MASK) !=
+ (next->cmd_flags & REQ_FAILFAST_MASK)) {
+ blk_rq_set_mixed_merge(req);
+ blk_rq_set_mixed_merge(next);
+ }
+
+ /*
* At this point we have either done a back merge
* or front merge. We need the smaller start_time of
* the merged requests to be the current request
diff --git a/block/blk-settings.c b/block/blk-settings.c
index 476d8706507..83413ff8373 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -428,6 +428,25 @@ void blk_queue_io_min(struct request_queue *q, unsigned int min)
EXPORT_SYMBOL(blk_queue_io_min);
/**
+ * blk_limits_io_opt - set optimal request size for a device
+ * @limits: the queue limits
+ * @opt: smallest I/O size in bytes
+ *
+ * Description:
+ * Storage devices may report an optimal I/O size, which is the
+ * device's preferred unit for sustained I/O. This is rarely reported
+ * for disk drives. For RAID arrays it is usually the stripe width or
+ * the internal track size. A properly aligned multiple of
+ * optimal_io_size is the preferred request size for workloads where
+ * sustained throughput is desired.
+ */
+void blk_limits_io_opt(struct queue_limits *limits, unsigned int opt)
+{
+ limits->io_opt = opt;
+}
+EXPORT_SYMBOL(blk_limits_io_opt);
+
+/**
* blk_queue_io_opt - set optimal request size for the queue
* @q: the request queue for the device
* @opt: optimal request size in bytes
@@ -442,7 +461,7 @@ EXPORT_SYMBOL(blk_queue_io_min);
*/
void blk_queue_io_opt(struct request_queue *q, unsigned int opt)
{
- q->limits.io_opt = opt;
+ blk_limits_io_opt(&q->limits, opt);
}
EXPORT_SYMBOL(blk_queue_io_opt);
diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c
index d3aa2aadb3e..b78c9c3e267 100644
--- a/block/blk-sysfs.c
+++ b/block/blk-sysfs.c
@@ -40,7 +40,12 @@ queue_requests_store(struct request_queue *q, const char *page, size_t count)
{
struct request_list *rl = &q->rq;
unsigned long nr;
- int ret = queue_var_store(&nr, page, count);
+ int ret;
+
+ if (!q->request_fn)
+ return -EINVAL;
+
+ ret = queue_var_store(&nr, page, count);
if (nr < BLKDEV_MIN_RQ)
nr = BLKDEV_MIN_RQ;
diff --git a/block/blk.h b/block/blk.h
index 3fae6add543..5ee3d7e72fe 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -104,6 +104,7 @@ int ll_front_merge_fn(struct request_queue *q, struct request *req,
int attempt_back_merge(struct request_queue *q, struct request *rq);
int attempt_front_merge(struct request_queue *q, struct request *rq);
void blk_recalc_rq_segments(struct request *rq);
+void blk_rq_set_mixed_merge(struct request *rq);
void blk_queue_congestion_threshold(struct request_queue *q);
diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c
index fd7080ed793..0e3814b662a 100644
--- a/block/cfq-iosched.c
+++ b/block/cfq-iosched.c
@@ -134,13 +134,8 @@ struct cfq_data {
struct rb_root prio_trees[CFQ_PRIO_LISTS];
unsigned int busy_queues;
- /*
- * Used to track any pending rt requests so we can pre-empt current
- * non-RT cfqq in service when this value is non-zero.
- */
- unsigned int busy_rt_queues;
- int rq_in_driver;
+ int rq_in_driver[2];
int sync_flight;
/*
@@ -191,7 +186,6 @@ enum cfqq_state_flags {
CFQ_CFQQ_FLAG_on_rr = 0, /* on round-robin busy list */
CFQ_CFQQ_FLAG_wait_request, /* waiting for a request */
CFQ_CFQQ_FLAG_must_dispatch, /* must be allowed a dispatch */
- CFQ_CFQQ_FLAG_must_alloc, /* must be allowed rq alloc */
CFQ_CFQQ_FLAG_must_alloc_slice, /* per-slice must_alloc flag */
CFQ_CFQQ_FLAG_fifo_expire, /* FIFO checked in this slice */
CFQ_CFQQ_FLAG_idle_window, /* slice idling enabled */
@@ -218,7 +212,6 @@ static inline int cfq_cfqq_##name(const struct cfq_queue *cfqq) \
CFQ_CFQQ_FNS(on_rr);
CFQ_CFQQ_FNS(wait_request);
CFQ_CFQQ_FNS(must_dispatch);
-CFQ_CFQQ_FNS(must_alloc);
CFQ_CFQQ_FNS(must_alloc_slice);
CFQ_CFQQ_FNS(fifo_expire);
CFQ_CFQQ_FNS(idle_window);
@@ -239,6 +232,11 @@ static struct cfq_queue *cfq_get_queue(struct cfq_data *, int,
static struct cfq_io_context *cfq_cic_lookup(struct cfq_data *,
struct io_context *);
+static inline int rq_in_driver(struct cfq_data *cfqd)
+{
+ return cfqd->rq_in_driver[0] + cfqd->rq_in_driver[1];
+}
+
static inline struct cfq_queue *cic_to_cfqq(struct cfq_io_context *cic,
int is_sync)
{
@@ -257,7 +255,7 @@ static inline void cic_set_cfqq(struct cfq_io_context *cic,
*/
static inline int cfq_bio_sync(struct bio *bio)
{
- if (bio_data_dir(bio) == READ || bio_sync(bio))
+ if (bio_data_dir(bio) == READ || bio_rw_flagged(bio, BIO_RW_SYNCIO))
return 1;
return 0;
@@ -648,8 +646,6 @@ static void cfq_add_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
BUG_ON(cfq_cfqq_on_rr(cfqq));
cfq_mark_cfqq_on_rr(cfqq);
cfqd->busy_queues++;
- if (cfq_class_rt(cfqq))
- cfqd->busy_rt_queues++;
cfq_resort_rr_list(cfqd, cfqq);
}
@@ -673,8 +669,6 @@ static void cfq_del_cfqq_rr(struct cfq_data *cfqd, struct cfq_queue *cfqq)
BUG_ON(!cfqd->busy_queues);
cfqd->busy_queues--;
- if (cfq_class_rt(cfqq))
- cfqd->busy_rt_queues--;
}
/*
@@ -760,9 +754,9 @@ static void cfq_activate_request(struct request_queue *q, struct request *rq)
{
struct cfq_data *cfqd = q->elevator->elevator_data;
- cfqd->rq_in_driver++;
+ cfqd->rq_in_driver[rq_is_sync(rq)]++;
cfq_log_cfqq(cfqd, RQ_CFQQ(rq), "activate rq, drv=%d",
- cfqd->rq_in_driver);
+ rq_in_driver(cfqd));
cfqd->last_position = blk_rq_pos(rq) + blk_rq_sectors(rq);
}
@@ -770,11 +764,12 @@ static void cfq_activate_request(struct request_queue *q, struct request *rq)
static void cfq_deactivate_request(struct request_queue *q, struct request *rq)
{
struct cfq_data *cfqd = q->elevator->elevator_data;
+ const int sync = rq_is_sync(rq);
- WARN_ON(!cfqd->rq_in_driver);
- cfqd->rq_in_driver--;
+ WARN_ON(!cfqd->rq_in_driver[sync]);
+ cfqd->rq_in_driver[sync]--;
cfq_log_cfqq(cfqd, RQ_CFQQ(rq), "deactivate rq, drv=%d",
- cfqd->rq_in_driver);
+ rq_in_driver(cfqd));
}
static void cfq_remove_request(struct request *rq)
@@ -1080,7 +1075,7 @@ static void cfq_arm_slice_timer(struct cfq_data *cfqd)
/*
* still requests with the driver, don't idle
*/
- if (cfqd->rq_in_driver)
+ if (rq_in_driver(cfqd))
return;
/*
@@ -1115,6 +1110,7 @@ static void cfq_dispatch_insert(struct request_queue *q, struct request *rq)
cfq_log_cfqq(cfqd, cfqq, "dispatch_insert");
+ cfqq->next_rq = cfq_find_next_rq(cfqd, cfqq, rq);
cfq_remove_request(rq);
cfqq->dispatched++;
elv_dispatch_sort(q, rq);
@@ -1179,20 +1175,6 @@ static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd)
goto expire;
/*
- * If we have a RT cfqq waiting, then we pre-empt the current non-rt
- * cfqq.
- */
- if (!cfq_class_rt(cfqq) && cfqd->busy_rt_queues) {
- /*
- * We simulate this as cfqq timed out so that it gets to bank
- * the remaining of its time slice.
- */
- cfq_log_cfqq(cfqd, cfqq, "preempt");
- cfq_slice_expired(cfqd, 1);
- goto new_queue;
- }
-
- /*
* The active queue has requests and isn't expired, allow it to
* dispatch.
*/
@@ -1312,6 +1294,12 @@ static int cfq_dispatch_requests(struct request_queue *q, int force)
return 0;
/*
+ * Drain async requests before we start sync IO
+ */
+ if (cfq_cfqq_idle_window(cfqq) && cfqd->rq_in_driver[BLK_RW_ASYNC])
+ return 0;
+
+ /*
* If this is an async queue and we have sync IO in flight, let it wait
*/
if (cfqd->sync_flight && !cfq_cfqq_sync(cfqq))
@@ -1362,7 +1350,7 @@ static int cfq_dispatch_requests(struct request_queue *q, int force)
cfq_slice_expired(cfqd, 0);
}
- cfq_log(cfqd, "dispatched a request");
+ cfq_log_cfqq(cfqd, cfqq, "dispatched a request");
return 1;
}
@@ -2130,11 +2118,11 @@ static void cfq_insert_request(struct request_queue *q, struct request *rq)
*/
static void cfq_update_hw_tag(struct cfq_data *cfqd)
{
- if (cfqd->rq_in_driver > cfqd->rq_in_driver_peak)
- cfqd->rq_in_driver_peak = cfqd->rq_in_driver;
+ if (rq_in_driver(cfqd) > cfqd->rq_in_driver_peak)
+ cfqd->rq_in_driver_peak = rq_in_driver(cfqd);
if (cfqd->rq_queued <= CFQ_HW_QUEUE_MIN &&
- cfqd->rq_in_driver <= CFQ_HW_QUEUE_MIN)
+ rq_in_driver(cfqd) <= CFQ_HW_QUEUE_MIN)
return;
if (cfqd->hw_tag_samples++ < 50)
@@ -2161,9 +2149,9 @@ static void cfq_completed_request(struct request_queue *q, struct request *rq)
cfq_update_hw_tag(cfqd);
- WARN_ON(!cfqd->rq_in_driver);
+ WARN_ON(!cfqd->rq_in_driver[sync]);
WARN_ON(!cfqq->dispatched);
- cfqd->rq_in_driver--;
+ cfqd->rq_in_driver[sync]--;
cfqq->dispatched--;
if (cfq_cfqq_sync(cfqq))
@@ -2197,7 +2185,7 @@ static void cfq_completed_request(struct request_queue *q, struct request *rq)
cfq_arm_slice_timer(cfqd);
}
- if (!cfqd->rq_in_driver)
+ if (!rq_in_driver(cfqd))
cfq_schedule_dispatch(cfqd);
}
@@ -2229,8 +2217,7 @@ static void cfq_prio_boost(struct cfq_queue *cfqq)
static inline int __cfq_may_queue(struct cfq_queue *cfqq)
{
- if ((cfq_cfqq_wait_request(cfqq) || cfq_cfqq_must_alloc(cfqq)) &&
- !cfq_cfqq_must_alloc_slice(cfqq)) {
+ if (cfq_cfqq_wait_request(cfqq) && !cfq_cfqq_must_alloc_slice(cfqq)) {
cfq_mark_cfqq_must_alloc_slice(cfqq);
return ELV_MQUEUE_MUST;
}
@@ -2317,7 +2304,6 @@ cfq_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask)
}
cfqq->allocated[rw]++;
- cfq_clear_cfqq_must_alloc(cfqq);
atomic_inc(&cfqq->ref);
spin_unlock_irqrestore(q->queue_lock, flags);
diff --git a/block/elevator.c b/block/elevator.c
index 2d511f9105e..1975b619c86 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -79,7 +79,8 @@ int elv_rq_merge_ok(struct request *rq, struct bio *bio)
/*
* Don't merge file system requests and discard requests
*/
- if (bio_discard(bio) != bio_discard(rq->bio))
+ if (bio_rw_flagged(bio, BIO_RW_DISCARD) !=
+ bio_rw_flagged(rq->bio, BIO_RW_DISCARD))
return 0;
/*
@@ -100,19 +101,6 @@ int elv_rq_merge_ok(struct request *rq, struct bio *bio)
if (bio_integrity(bio) != blk_integrity_rq(rq))
return 0;
- /*
- * Don't merge if failfast settings don't match.
- *
- * FIXME: The negation in front of each condition is necessary
- * because bio and request flags use different bit positions
- * and the accessors return those bits directly. This
- * ugliness will soon go away.
- */
- if (!bio_failfast_dev(bio) != !blk_failfast_dev(rq) ||
- !bio_failfast_transport(bio) != !blk_failfast_transport(rq) ||
- !bio_failfast_driver(bio) != !blk_failfast_driver(rq))
- return 0;
-
if (!elv_iosched_allow_merge(rq, bio))
return 0;
diff --git a/block/genhd.c b/block/genhd.c
index f4c64c2b303..5b76bf55d05 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -869,6 +869,7 @@ static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL);
static DEVICE_ATTR(alignment_offset, S_IRUGO, disk_alignment_offset_show, NULL);
static DEVICE_ATTR(capability, S_IRUGO, disk_capability_show, NULL);
static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL);
+static DEVICE_ATTR(inflight, S_IRUGO, part_inflight_show, NULL);
#ifdef CONFIG_FAIL_MAKE_REQUEST
static struct device_attribute dev_attr_fail =
__ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store);
@@ -888,6 +889,7 @@ static struct attribute *disk_attrs[] = {
&dev_attr_alignment_offset.attr,
&dev_attr_capability.attr,
&dev_attr_stat.attr,
+ &dev_attr_inflight.attr,
#ifdef CONFIG_FAIL_MAKE_REQUEST
&dev_attr_fail.attr,
#endif
@@ -1053,7 +1055,7 @@ static int diskstats_show(struct seq_file *seqf, void *v)
part_stat_read(hd, merges[1]),
(unsigned long long)part_stat_read(hd, sectors[1]),
jiffies_to_msecs(part_stat_read(hd, ticks[1])),
- hd->in_flight,
+ part_in_flight(hd),
jiffies_to_msecs(part_stat_read(hd, io_ticks)),
jiffies_to_msecs(part_stat_read(hd, time_in_queue))
);
@@ -1215,6 +1217,16 @@ void put_disk(struct gendisk *disk)
EXPORT_SYMBOL(put_disk);
+static void set_disk_ro_uevent(struct gendisk *gd, int ro)
+{
+ char event[] = "DISK_RO=1";
+ char *envp[] = { event, NULL };
+
+ if (!ro)
+ event[8] = '0';
+ kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp);
+}
+
void set_device_ro(struct block_device *bdev, int flag)
{
bdev->bd_part->policy = flag;
@@ -1227,8 +1239,12 @@ void set_disk_ro(struct gendisk *disk, int flag)
struct disk_part_iter piter;
struct hd_struct *part;
- disk_part_iter_init(&piter, disk,
- DISK_PITER_INCL_EMPTY | DISK_PITER_INCL_PART0);
+ if (disk->part0.policy != flag) {
+ set_disk_ro_uevent(disk, flag);
+ disk->part0.policy = flag;
+ }
+
+ disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY);
while ((part = disk_part_iter_next(&piter)))
part->policy = flag;
disk_part_iter_exit(&piter);
diff --git a/block/ioctl.c b/block/ioctl.c
index 500e4c73cc5..d3e6b5827a3 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -112,22 +112,9 @@ static int blkdev_reread_part(struct block_device *bdev)
return res;
}
-static void blk_ioc_discard_endio(struct bio *bio, int err)
-{
- if (err) {
- if (err == -EOPNOTSUPP)
- set_bit(BIO_EOPNOTSUPP, &bio->bi_flags);
- clear_bit(BIO_UPTODATE, &bio->bi_flags);
- }
- complete(bio->bi_private);
-}
-
static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
uint64_t len)
{
- struct request_queue *q = bdev_get_queue(bdev);
- int ret = 0;
-
if (start & 511)
return -EINVAL;
if (len & 511)
@@ -137,40 +124,8 @@ static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
if (start + len > (bdev->bd_inode->i_size >> 9))
return -EINVAL;
-
- if (!q->prepare_discard_fn)
- return -EOPNOTSUPP;
-
- while (len && !ret) {
- DECLARE_COMPLETION_ONSTACK(wait);
- struct bio *bio;
-
- bio = bio_alloc(GFP_KERNEL, 0);
-
- bio->bi_end_io = blk_ioc_discard_endio;
- bio->bi_bdev = bdev;
- bio->bi_private = &wait;
- bio->bi_sector = start;
-
- if (len > queue_max_hw_sectors(q)) {
- bio->bi_size = queue_max_hw_sectors(q) << 9;
- len -= queue_max_hw_sectors(q);
- start += queue_max_hw_sectors(q);
- } else {
- bio->bi_size = len << 9;
- len = 0;
- }
- submit_bio(DISCARD_NOBARRIER, bio);
-
- wait_for_completion(&wait);
-
- if (bio_flagged(bio, BIO_EOPNOTSUPP))
- ret = -EOPNOTSUPP;
- else if (!bio_flagged(bio, BIO_UPTODATE))
- ret = -EIO;
- bio_put(bio);
- }
- return ret;
+ return blkdev_issue_discard(bdev, start, len, GFP_KERNEL,
+ DISCARD_FL_WAIT);
}
static int put_ushort(unsigned long arg, unsigned short val)
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 24665067301..f60b2b6a093 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -204,6 +204,7 @@ static void amba_device_release(struct device *dev)
int amba_device_register(struct amba_device *dev, struct resource *parent)
{
u32 pid, cid;
+ u32 size;
void __iomem *tmp;
int i, ret;
@@ -229,16 +230,25 @@ int amba_device_register(struct amba_device *dev, struct resource *parent)
if (ret)
goto err_out;
- tmp = ioremap(dev->res.start, SZ_4K);
+ /*
+ * Dynamically calculate the size of the resource
+ * and use this for iomap
+ */
+ size = resource_size(&dev->res);
+ tmp = ioremap(dev->res.start, size);
if (!tmp) {
ret = -ENOMEM;
goto err_release;
}
+ /*
+ * Read pid and cid based on size of resource
+ * they are located at end of region
+ */
for (pid = 0, i = 0; i < 4; i++)
- pid |= (readl(tmp + 0xfe0 + 4 * i) & 255) << (i * 8);
+ pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << (i * 8);
for (cid = 0, i = 0; i < 4; i++)
- cid |= (readl(tmp + 0xff0 + 4 * i) & 255) << (i * 8);
+ cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8);
iounmap(tmp);
@@ -353,11 +363,14 @@ amba_find_device(const char *busid, struct device *parent, unsigned int id,
int amba_request_regions(struct amba_device *dev, const char *name)
{
int ret = 0;
+ u32 size;
if (!name)
name = dev->dev.driver->name;
- if (!request_mem_region(dev->res.start, SZ_4K, name))
+ size = resource_size(&dev->res);
+
+ if (!request_mem_region(dev->res.start, size, name))
ret = -EBUSY;
return ret;
@@ -371,7 +384,10 @@ int amba_request_regions(struct amba_device *dev, const char *name)
*/
void amba_release_regions(struct amba_device *dev)
{
- release_mem_region(dev->res.start, SZ_4K);
+ u32 size;
+
+ size = resource_size(&dev->res);
+ release_mem_region(dev->res.start, size);
}
EXPORT_SYMBOL(amba_driver_register);
diff --git a/drivers/block/aoe/aoeblk.c b/drivers/block/aoe/aoeblk.c
index 95d344971ed..b6cd571adbf 100644
--- a/drivers/block/aoe/aoeblk.c
+++ b/drivers/block/aoe/aoeblk.c
@@ -172,6 +172,9 @@ aoeblk_make_request(struct request_queue *q, struct bio *bio)
BUG();
bio_endio(bio, -ENXIO);
return 0;
+ } else if (bio_rw_flagged(bio, BIO_RW_BARRIER)) {
+ bio_endio(bio, -EOPNOTSUPP);
+ return 0;
} else if (bio->bi_io_vec == NULL) {
printk(KERN_ERR "aoe: bi_io_vec is NULL\n");
BUG();
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index a52cc7fe45e..0589dfbbd7d 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -3889,7 +3889,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
int j = 0;
int rc;
int dac, return_code;
- InquiryData_struct *inq_buff = NULL;
+ InquiryData_struct *inq_buff;
if (reset_devices) {
/* Reset the controller with a PCI power-cycle */
@@ -4029,6 +4029,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
printk(KERN_WARNING "cciss: unable to determine firmware"
" version of controller\n");
}
+ kfree(inq_buff);
cciss_procinit(i);
@@ -4045,7 +4046,6 @@ static int __devinit cciss_init_one(struct pci_dev *pdev,
return 1;
clean4:
- kfree(inq_buff);
kfree(hba[i]->cmd_pool_bits);
if (hba[i]->cmd_pool)
pci_free_consistent(hba[i]->pdev,
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 5757188cd1f..bbb79441d89 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -475,7 +475,7 @@ static int do_bio_filebacked(struct loop_device *lo, struct bio *bio)
pos = ((loff_t) bio->bi_sector << 9) + lo->lo_offset;
if (bio_rw(bio) == WRITE) {
- int barrier = bio_barrier(bio);
+ bool barrier = bio_rw_flagged(bio, BIO_RW_BARRIER);
struct file *file = lo->lo_backing_file;
if (barrier) {
diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
index 911dfd98d81..9f3518c515a 100644
--- a/drivers/block/paride/pcd.c
+++ b/drivers/block/paride/pcd.c
@@ -219,8 +219,6 @@ static int pcd_sector; /* address of next requested sector */
static int pcd_count; /* number of blocks still to do */
static char *pcd_buf; /* buffer for request in progress */
-static int pcd_warned; /* Have we logged a phase warning ? */
-
/* kernel glue structures */
static int pcd_block_open(struct block_device *bdev, fmode_t mode)
@@ -417,12 +415,10 @@ static int pcd_completion(struct pcd_unit *cd, char *buf, char *fun)
printk
("%s: %s: Unexpected phase %d, d=%d, k=%d\n",
cd->name, fun, p, d, k);
- if ((verbose < 2) && !pcd_warned) {
- pcd_warned = 1;
- printk
- ("%s: WARNING: ATAPI phase errors\n",
- cd->name);
- }
+ if (verbose < 2)
+ printk_once(
+ "%s: WARNING: ATAPI phase errors\n",
+ cd->name);
mdelay(1);
}
if (k++ > PCD_TMO) {
diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c
index da403b6a7f4..f5cd2e83ebc 100644
--- a/drivers/block/sx8.c
+++ b/drivers/block/sx8.c
@@ -1564,15 +1564,13 @@ static int carm_init_shm(struct carm_host *host)
static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
- static unsigned int printed_version;
struct carm_host *host;
unsigned int pci_dac;
int rc;
struct request_queue *q;
unsigned int i;
- if (!printed_version++)
- printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+ printk_once(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
rc = pci_enable_device(pdev);
if (rc)
diff --git a/drivers/block/viodasd.c b/drivers/block/viodasd.c
index 390d69bb7c4..b441ce3832e 100644
--- a/drivers/block/viodasd.c
+++ b/drivers/block/viodasd.c
@@ -416,15 +416,9 @@ retry:
goto retry;
}
if (we.max_disk > (MAX_DISKNO - 1)) {
- static int warned;
-
- if (warned == 0) {
- warned++;
- printk(VIOD_KERN_INFO
- "Only examining the first %d "
- "of %d disks connected\n",
- MAX_DISKNO, we.max_disk + 1);
- }
+ printk_once(VIOD_KERN_INFO
+ "Only examining the first %d of %d disks connected\n",
+ MAX_DISKNO, we.max_disk + 1);
}
/* Send the close event to OS/400. We DON'T expect a response */
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index 05f9d18b936..40268db02e2 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -246,7 +246,7 @@ static const struct file_operations raw_fops = {
.read = do_sync_read,
.aio_read = generic_file_aio_read,
.write = do_sync_write,
- .aio_write = generic_file_aio_write_nolock,
+ .aio_write = blkdev_aio_write,
.open = raw_open,
.release= raw_release,
.ioctl = raw_ioctl,
diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c
index 51e7a46787b..5942a9d674c 100644
--- a/drivers/char/serial167.c
+++ b/drivers/char/serial167.c
@@ -171,7 +171,6 @@ static int startup(struct cyclades_port *);
static void cy_throttle(struct tty_struct *);
static void cy_unthrottle(struct tty_struct *);
static void config_setup(struct cyclades_port *);
-extern void console_print(const char *);
#ifdef CYCLOM_SHOW_STATUS
static void show_status(int);
#endif
@@ -245,7 +244,7 @@ void SP(char *data)
{
unsigned long flags;
local_irq_save(flags);
- console_print(data);
+ printk(KERN_EMERG "%s", data);
local_irq_restore(flags);
}
@@ -255,7 +254,7 @@ void CP(char data)
unsigned long flags;
local_irq_save(flags);
scrn[0] = data;
- console_print(scrn);
+ printk(KERN_EMERG "%c", scrn);
local_irq_restore(flags);
} /* CP */
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 98aa4a7db41..cfa033ce53a 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -17,6 +17,10 @@ ifdef CONFIG_PCI
edac_core-objs += edac_pci.o edac_pci_sysfs.o
endif
+ifdef CONFIG_CPU_SUP_AMD
+edac_core-objs += edac_mce_amd.o
+endif
+
obj-$(CONFIG_EDAC_AMD76X) += amd76x_edac.o
obj-$(CONFIG_EDAC_CPC925) += cpc925_edac.o
obj-$(CONFIG_EDAC_I5000) += i5000_edac.o
@@ -32,7 +36,7 @@ obj-$(CONFIG_EDAC_X38) += x38_edac.o
obj-$(CONFIG_EDAC_I82860) += i82860_edac.o
obj-$(CONFIG_EDAC_R82600) += r82600_edac.o
-amd64_edac_mod-y := amd64_edac_err_types.o amd64_edac.o
+amd64_edac_mod-y := amd64_edac.o
amd64_edac_mod-$(CONFIG_EDAC_DEBUG) += amd64_edac_dbg.o
amd64_edac_mod-$(CONFIG_EDAC_AMD64_ERROR_INJECTION) += amd64_edac_inj.o
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index e2a10bcba7a..173dc4a8416 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -19,6 +19,63 @@ static struct mem_ctl_info *mci_lookup[MAX_NUMNODES];
static struct amd64_pvt *pvt_lookup[MAX_NUMNODES];
/*
+ * See F2x80 for K8 and F2x[1,0]80 for Fam10 and later. The table below is only
+ * for DDR2 DRAM mapping.
+ */
+u32 revf_quad_ddr2_shift[] = {
+ 0, /* 0000b NULL DIMM (128mb) */
+ 28, /* 0001b 256mb */
+ 29, /* 0010b 512mb */
+ 29, /* 0011b 512mb */
+ 29, /* 0100b 512mb */
+ 30, /* 0101b 1gb */
+ 30, /* 0110b 1gb */
+ 31, /* 0111b 2gb */
+ 31, /* 1000b 2gb */
+ 32, /* 1001b 4gb */
+ 32, /* 1010b 4gb */
+ 33, /* 1011b 8gb */
+ 0, /* 1100b future */
+ 0, /* 1101b future */
+ 0, /* 1110b future */
+ 0 /* 1111b future */
+};
+
+/*
+ * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
+ * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
+ * or higher value'.
+ *
+ *FIXME: Produce a better mapping/linearisation.
+ */
+
+struct scrubrate scrubrates[] = {
+ { 0x01, 1600000000UL},
+ { 0x02, 800000000UL},
+ { 0x03, 400000000UL},
+ { 0x04, 200000000UL},
+ { 0x05, 100000000UL},
+ { 0x06, 50000000UL},
+ { 0x07, 25000000UL},
+ { 0x08, 12284069UL},
+ { 0x09, 6274509UL},
+ { 0x0A, 3121951UL},
+ { 0x0B, 1560975UL},
+ { 0x0C, 781440UL},
+ { 0x0D, 390720UL},
+ { 0x0E, 195300UL},
+ { 0x0F, 97650UL},
+ { 0x10, 48854UL},
+ { 0x11, 24427UL},
+ { 0x12, 12213UL},
+ { 0x13, 6101UL},
+ { 0x14, 3051UL},
+ { 0x15, 1523UL},
+ { 0x16, 761UL},
+ { 0x00, 0UL}, /* scrubbing off */
+};
+
+/*
* Memory scrubber control interface. For K8, memory scrubbing is handled by
* hardware and can involve L2 cache, dcache as well as the main memory. With
* F10, this is extended to L3 cache scrubbing on CPU models sporting that
@@ -693,7 +750,7 @@ static void find_csrow_limits(struct mem_ctl_info *mci, int csrow,
* specific.
*/
static u64 extract_error_address(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info)
+ struct err_regs *info)
{
struct amd64_pvt *pvt = mci->pvt_info;
@@ -1049,7 +1106,7 @@ static int k8_early_channel_count(struct amd64_pvt *pvt)
/* extract the ERROR ADDRESS for the K8 CPUs */
static u64 k8_get_error_address(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info)
+ struct err_regs *info)
{
return (((u64) (info->nbeah & 0xff)) << 32) +
(info->nbeal & ~0x03);
@@ -1092,7 +1149,7 @@ static void k8_read_dram_base_limit(struct amd64_pvt *pvt, int dram)
}
static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info,
+ struct err_regs *info,
u64 SystemAddress)
{
struct mem_ctl_info *src_mci;
@@ -1101,8 +1158,8 @@ static void k8_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
u32 page, offset;
/* Extract the syndrome parts and form a 16-bit syndrome */
- syndrome = EXTRACT_HIGH_SYNDROME(info->nbsl) << 8;
- syndrome |= EXTRACT_LOW_SYNDROME(info->nbsh);
+ syndrome = HIGH_SYNDROME(info->nbsl) << 8;
+ syndrome |= LOW_SYNDROME(info->nbsh);
/* CHIPKILL enabled */
if (info->nbcfg & K8_NBCFG_CHIPKILL) {
@@ -1311,7 +1368,7 @@ static void amd64_teardown(struct amd64_pvt *pvt)
}
static u64 f10_get_error_address(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info)
+ struct err_regs *info)
{
return (((u64) (info->nbeah & 0xffff)) << 32) +
(info->nbeal & ~0x01);
@@ -1688,7 +1745,7 @@ static int f10_translate_sysaddr_to_cs(struct amd64_pvt *pvt, u64 sys_addr,
* The @sys_addr is usually an error address received from the hardware.
*/
static void f10_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info,
+ struct err_regs *info,
u64 sys_addr)
{
struct amd64_pvt *pvt = mci->pvt_info;
@@ -1701,8 +1758,8 @@ static void f10_map_sysaddr_to_csrow(struct mem_ctl_info *mci,
if (csrow >= 0) {
error_address_to_page_and_offset(sys_addr, &page, &offset);
- syndrome = EXTRACT_HIGH_SYNDROME(info->nbsl) << 8;
- syndrome |= EXTRACT_LOW_SYNDROME(info->nbsh);
+ syndrome = HIGH_SYNDROME(info->nbsl) << 8;
+ syndrome |= LOW_SYNDROME(info->nbsh);
/*
* Is CHIPKILL on? If so, then we can attempt to use the
@@ -2045,7 +2102,7 @@ static int get_channel_from_ecc_syndrome(unsigned short syndrome)
* - 0: if no valid error is indicated
*/
static int amd64_get_error_info_regs(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *regs)
+ struct err_regs *regs)
{
struct amd64_pvt *pvt;
struct pci_dev *misc_f3_ctl;
@@ -2094,10 +2151,10 @@ err_reg:
* - 0: if no error is found
*/
static int amd64_get_error_info(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info)
+ struct err_regs *info)
{
struct amd64_pvt *pvt;
- struct amd64_error_info_regs regs;
+ struct err_regs regs;
pvt = mci->pvt_info;
@@ -2152,48 +2209,12 @@ static int amd64_get_error_info(struct mem_ctl_info *mci,
return 1;
}
-static inline void amd64_decode_gart_tlb_error(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info)
-{
- u32 err_code;
- u32 ec_tt; /* error code transaction type (2b) */
- u32 ec_ll; /* error code cache level (2b) */
-
- err_code = EXTRACT_ERROR_CODE(info->nbsl);
- ec_ll = EXTRACT_LL_CODE(err_code);
- ec_tt = EXTRACT_TT_CODE(err_code);
-
- amd64_mc_printk(mci, KERN_ERR,
- "GART TLB event: transaction type(%s), "
- "cache level(%s)\n", tt_msgs[ec_tt], ll_msgs[ec_ll]);
-}
-
-static inline void amd64_decode_mem_cache_error(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info)
-{
- u32 err_code;
- u32 ec_rrrr; /* error code memory transaction (4b) */
- u32 ec_tt; /* error code transaction type (2b) */
- u32 ec_ll; /* error code cache level (2b) */
-
- err_code = EXTRACT_ERROR_CODE(info->nbsl);
- ec_ll = EXTRACT_LL_CODE(err_code);
- ec_tt = EXTRACT_TT_CODE(err_code);
- ec_rrrr = EXTRACT_RRRR_CODE(err_code);
-
- amd64_mc_printk(mci, KERN_ERR,
- "cache hierarchy error: memory transaction type(%s), "
- "transaction type(%s), cache level(%s)\n",
- rrrr_msgs[ec_rrrr], tt_msgs[ec_tt], ll_msgs[ec_ll]);
-}
-
-
/*
* Handle any Correctable Errors (CEs) that have occurred. Check for valid ERROR
* ADDRESS and process.
*/
static void amd64_handle_ce(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info)
+ struct err_regs *info)
{
struct amd64_pvt *pvt = mci->pvt_info;
u64 SystemAddress;
@@ -2216,7 +2237,7 @@ static void amd64_handle_ce(struct mem_ctl_info *mci,
/* Handle any Un-correctable Errors (UEs) */
static void amd64_handle_ue(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info)
+ struct err_regs *info)
{
int csrow;
u64 SystemAddress;
@@ -2261,59 +2282,24 @@ static void amd64_handle_ue(struct mem_ctl_info *mci,
}
}
-static void amd64_decode_bus_error(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info)
+static inline void __amd64_decode_bus_error(struct mem_ctl_info *mci,
+ struct err_regs *info)
{
- u32 err_code, ext_ec;
- u32 ec_pp; /* error code participating processor (2p) */
- u32 ec_to; /* error code timed out (1b) */
- u32 ec_rrrr; /* error code memory transaction (4b) */
- u32 ec_ii; /* error code memory or I/O (2b) */
- u32 ec_ll; /* error code cache level (2b) */
+ u32 ec = ERROR_CODE(info->nbsl);
+ u32 xec = EXT_ERROR_CODE(info->nbsl);
+ int ecc_type = info->nbsh & (0x3 << 13);
- ext_ec = EXTRACT_EXT_ERROR_CODE(info->nbsl);
- err_code = EXTRACT_ERROR_CODE(info->nbsl);
-
- ec_ll = EXTRACT_LL_CODE(err_code);
- ec_ii = EXTRACT_II_CODE(err_code);
- ec_rrrr = EXTRACT_RRRR_CODE(err_code);
- ec_to = EXTRACT_TO_CODE(err_code);
- ec_pp = EXTRACT_PP_CODE(err_code);
-
- amd64_mc_printk(mci, KERN_ERR,
- "BUS ERROR:\n"
- " time-out(%s) mem or i/o(%s)\n"
- " participating processor(%s)\n"
- " memory transaction type(%s)\n"
- " cache level(%s) Error Found by: %s\n",
- to_msgs[ec_to],
- ii_msgs[ec_ii],
- pp_msgs[ec_pp],
- rrrr_msgs[ec_rrrr],
- ll_msgs[ec_ll],
- (info->nbsh & K8_NBSH_ERR_SCRUBER) ?
- "Scrubber" : "Normal Operation");
-
- /* If this was an 'observed' error, early out */
- if (ec_pp == K8_NBSL_PP_OBS)
- return; /* We aren't the node involved */
-
- /* Parse out the extended error code for ECC events */
- switch (ext_ec) {
- /* F10 changed to one Extended ECC error code */
- case F10_NBSL_EXT_ERR_RES: /* Reserved field */
- case F10_NBSL_EXT_ERR_ECC: /* F10 ECC ext err code */
- break;
+ /* Bail early out if this was an 'observed' error */
+ if (PP(ec) == K8_NBSL_PP_OBS)
+ return;
- default:
- amd64_mc_printk(mci, KERN_ERR, "NOT ECC: no special error "
- "handling for this error\n");
+ /* Do only ECC errors */
+ if (xec && xec != F10_NBSL_EXT_ERR_ECC)
return;
- }
- if (info->nbsh & K8_NBSH_CECC)
+ if (ecc_type == 2)
amd64_handle_ce(mci, info);
- else if (info->nbsh & K8_NBSH_UECC)
+ else if (ecc_type == 1)
amd64_handle_ue(mci, info);
/*
@@ -2324,139 +2310,26 @@ static void amd64_decode_bus_error(struct mem_ctl_info *mci,
* catastrophic.
*/
if (info->nbsh & K8_NBSH_OVERFLOW)
- edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR
- "Error Overflow set");
+ edac_mc_handle_ce_no_info(mci, EDAC_MOD_STR "Error Overflow");
}
-int amd64_process_error_info(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info,
- int handle_errors)
+void amd64_decode_bus_error(int node_id, struct err_regs *regs)
{
- struct amd64_pvt *pvt;
- struct amd64_error_info_regs *regs;
- u32 err_code, ext_ec;
- int gart_tlb_error = 0;
-
- pvt = mci->pvt_info;
-
- /* If caller doesn't want us to process the error, return */
- if (!handle_errors)
- return 1;
-
- regs = info;
-
- debugf1("NorthBridge ERROR: mci(0x%p)\n", mci);
- debugf1(" MC node(%d) Error-Address(0x%.8x-%.8x)\n",
- pvt->mc_node_id, regs->nbeah, regs->nbeal);
- debugf1(" nbsh(0x%.8x) nbsl(0x%.8x)\n",
- regs->nbsh, regs->nbsl);
- debugf1(" Valid Error=%s Overflow=%s\n",
- (regs->nbsh & K8_NBSH_VALID_BIT) ? "True" : "False",
- (regs->nbsh & K8_NBSH_OVERFLOW) ? "True" : "False");
- debugf1(" Err Uncorrected=%s MCA Error Reporting=%s\n",
- (regs->nbsh & K8_NBSH_UNCORRECTED_ERR) ?
- "True" : "False",
- (regs->nbsh & K8_NBSH_ERR_ENABLE) ?
- "True" : "False");
- debugf1(" MiscErr Valid=%s ErrAddr Valid=%s PCC=%s\n",
- (regs->nbsh & K8_NBSH_MISC_ERR_VALID) ?
- "True" : "False",
- (regs->nbsh & K8_NBSH_VALID_ERROR_ADDR) ?
- "True" : "False",
- (regs->nbsh & K8_NBSH_PCC) ?
- "True" : "False");
- debugf1(" CECC=%s UECC=%s Found by Scruber=%s\n",
- (regs->nbsh & K8_NBSH_CECC) ?
- "True" : "False",
- (regs->nbsh & K8_NBSH_UECC) ?
- "True" : "False",
- (regs->nbsh & K8_NBSH_ERR_SCRUBER) ?
- "True" : "False");
- debugf1(" CORE0=%s CORE1=%s CORE2=%s CORE3=%s\n",
- (regs->nbsh & K8_NBSH_CORE0) ? "True" : "False",
- (regs->nbsh & K8_NBSH_CORE1) ? "True" : "False",
- (regs->nbsh & K8_NBSH_CORE2) ? "True" : "False",
- (regs->nbsh & K8_NBSH_CORE3) ? "True" : "False");
-
-
- err_code = EXTRACT_ERROR_CODE(regs->nbsl);
-
- /* Determine which error type:
- * 1) GART errors - non-fatal, developmental events
- * 2) MEMORY errors
- * 3) BUS errors
- * 4) Unknown error
- */
- if (TEST_TLB_ERROR(err_code)) {
- /*
- * GART errors are intended to help graphics driver developers
- * to detect bad GART PTEs. It is recommended by AMD to disable
- * GART table walk error reporting by default[1] (currently
- * being disabled in mce_cpu_quirks()) and according to the
- * comment in mce_cpu_quirks(), such GART errors can be
- * incorrectly triggered. We may see these errors anyway and
- * unless requested by the user, they won't be reported.
- *
- * [1] section 13.10.1 on BIOS and Kernel Developers Guide for
- * AMD NPT family 0Fh processors
- */
- if (report_gart_errors == 0)
- return 1;
-
- /*
- * Only if GART error reporting is requested should we generate
- * any logs.
- */
- gart_tlb_error = 1;
-
- debugf1("GART TLB error\n");
- amd64_decode_gart_tlb_error(mci, info);
- } else if (TEST_MEM_ERROR(err_code)) {
- debugf1("Memory/Cache error\n");
- amd64_decode_mem_cache_error(mci, info);
- } else if (TEST_BUS_ERROR(err_code)) {
- debugf1("Bus (Link/DRAM) error\n");
- amd64_decode_bus_error(mci, info);
- } else {
- /* shouldn't reach here! */
- amd64_mc_printk(mci, KERN_WARNING,
- "%s(): unknown MCE error 0x%x\n", __func__,
- err_code);
- }
-
- ext_ec = EXTRACT_EXT_ERROR_CODE(regs->nbsl);
- amd64_mc_printk(mci, KERN_ERR,
- "ExtErr=(0x%x) %s\n", ext_ec, ext_msgs[ext_ec]);
+ struct mem_ctl_info *mci = mci_lookup[node_id];
- if (((ext_ec >= F10_NBSL_EXT_ERR_CRC &&
- ext_ec <= F10_NBSL_EXT_ERR_TGT) ||
- (ext_ec == F10_NBSL_EXT_ERR_RMW)) &&
- EXTRACT_LDT_LINK(info->nbsh)) {
-
- amd64_mc_printk(mci, KERN_ERR,
- "Error on hypertransport link: %s\n",
- htlink_msgs[
- EXTRACT_LDT_LINK(info->nbsh)]);
- }
+ __amd64_decode_bus_error(mci, regs);
/*
* Check the UE bit of the NB status high register, if set generate some
* logs. If NOT a GART error, then process the event as a NO-INFO event.
* If it was a GART error, skip that process.
+ *
+ * FIXME: this should go somewhere else, if at all.
*/
- if (regs->nbsh & K8_NBSH_UNCORRECTED_ERR) {
- amd64_mc_printk(mci, KERN_CRIT, "uncorrected error\n");
- if (!gart_tlb_error)
- edac_mc_handle_ue_no_info(mci, "UE bit is set\n");
- }
-
- if (regs->nbsh & K8_NBSH_PCC)
- amd64_mc_printk(mci, KERN_CRIT,
- "PCC (processor context corrupt) set\n");
+ if (regs->nbsh & K8_NBSH_UC_ERR && !report_gart_errors)
+ edac_mc_handle_ue_no_info(mci, "UE bit is set");
- return 1;
}
-EXPORT_SYMBOL_GPL(amd64_process_error_info);
/*
* The main polling 'check' function, called FROM the edac core to perform the
@@ -2464,10 +2337,12 @@ EXPORT_SYMBOL_GPL(amd64_process_error_info);
*/
static void amd64_check(struct mem_ctl_info *mci)
{
- struct amd64_error_info_regs info;
+ struct err_regs regs;
- if (amd64_get_error_info(mci, &info))
- amd64_process_error_info(mci, &info, 1);
+ if (amd64_get_error_info(mci, &regs)) {
+ struct amd64_pvt *pvt = mci->pvt_info;
+ amd_decode_nb_mce(pvt->mc_node_id, &regs, 1);
+ }
}
/*
@@ -3163,6 +3038,13 @@ static int amd64_init_2nd_stage(struct amd64_pvt *pvt)
mci_lookup[node_id] = mci;
pvt_lookup[node_id] = NULL;
+
+ /* register stuff with EDAC MCE */
+ if (report_gart_errors)
+ amd_report_gart_errors(true);
+
+ amd_register_ecc_decoder(amd64_decode_bus_error);
+
return 0;
err_add_mc:
@@ -3229,6 +3111,10 @@ static void __devexit amd64_remove_one_instance(struct pci_dev *pdev)
mci_lookup[pvt->mc_node_id] = NULL;
+ /* unregister from EDAC MCE */
+ amd_report_gart_errors(false);
+ amd_unregister_ecc_decoder(amd64_decode_bus_error);
+
/* Free the EDAC CORE resources */
edac_mc_free(mci);
}
diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h
index ba73015af8e..8ea07e2715d 100644
--- a/drivers/edac/amd64_edac.h
+++ b/drivers/edac/amd64_edac.h
@@ -72,6 +72,7 @@
#include <linux/edac.h>
#include <asm/msr.h>
#include "edac_core.h"
+#include "edac_mce_amd.h"
#define amd64_printk(level, fmt, arg...) \
edac_printk(level, "amd64", fmt, ##arg)
@@ -303,21 +304,9 @@ enum {
#define K8_NBSL 0x48
-#define EXTRACT_HIGH_SYNDROME(x) (((x) >> 24) & 0xff)
-#define EXTRACT_EXT_ERROR_CODE(x) (((x) >> 16) & 0x1f)
-
/* Family F10h: Normalized Extended Error Codes */
#define F10_NBSL_EXT_ERR_RES 0x0
-#define F10_NBSL_EXT_ERR_CRC 0x1
-#define F10_NBSL_EXT_ERR_SYNC 0x2
-#define F10_NBSL_EXT_ERR_MST 0x3
-#define F10_NBSL_EXT_ERR_TGT 0x4
-#define F10_NBSL_EXT_ERR_GART 0x5
-#define F10_NBSL_EXT_ERR_RMW 0x6
-#define F10_NBSL_EXT_ERR_WDT 0x7
#define F10_NBSL_EXT_ERR_ECC 0x8
-#define F10_NBSL_EXT_ERR_DEV 0x9
-#define F10_NBSL_EXT_ERR_LINK_DATA 0xA
/* Next two are overloaded values */
#define F10_NBSL_EXT_ERR_LINK_PROTO 0xB
@@ -348,17 +337,6 @@ enum {
#define K8_NBSL_EXT_ERR_CHIPKILL_ECC 0x8
#define K8_NBSL_EXT_ERR_DRAM_PARITY 0xD
-#define EXTRACT_ERROR_CODE(x) ((x) & 0xffff)
-#define TEST_TLB_ERROR(x) (((x) & 0xFFF0) == 0x0010)
-#define TEST_MEM_ERROR(x) (((x) & 0xFF00) == 0x0100)
-#define TEST_BUS_ERROR(x) (((x) & 0xF800) == 0x0800)
-#define EXTRACT_TT_CODE(x) (((x) >> 2) & 0x3)
-#define EXTRACT_II_CODE(x) (((x) >> 2) & 0x3)
-#define EXTRACT_LL_CODE(x) (((x) >> 0) & 0x3)
-#define EXTRACT_RRRR_CODE(x) (((x) >> 4) & 0xf)
-#define EXTRACT_TO_CODE(x) (((x) >> 8) & 0x1)
-#define EXTRACT_PP_CODE(x) (((x) >> 9) & 0x3)
-
/*
* The following are for BUS type errors AFTER values have been normalized by
* shifting right
@@ -368,28 +346,7 @@ enum {
#define K8_NBSL_PP_OBS 0x2
#define K8_NBSL_PP_GENERIC 0x3
-
-#define K8_NBSH 0x4C
-
-#define K8_NBSH_VALID_BIT BIT(31)
-#define K8_NBSH_OVERFLOW BIT(30)
-#define K8_NBSH_UNCORRECTED_ERR BIT(29)
-#define K8_NBSH_ERR_ENABLE BIT(28)
-#define K8_NBSH_MISC_ERR_VALID BIT(27)
-#define K8_NBSH_VALID_ERROR_ADDR BIT(26)
-#define K8_NBSH_PCC BIT(25)
-#define K8_NBSH_CECC BIT(14)
-#define K8_NBSH_UECC BIT(13)
-#define K8_NBSH_ERR_SCRUBER BIT(8)
-#define K8_NBSH_CORE3 BIT(3)
-#define K8_NBSH_CORE2 BIT(2)
-#define K8_NBSH_CORE1 BIT(1)
-#define K8_NBSH_CORE0 BIT(0)
-
-#define EXTRACT_LDT_LINK(x) (((x) >> 4) & 0x7)
#define EXTRACT_ERR_CPU_MAP(x) ((x) & 0xF)
-#define EXTRACT_LOW_SYNDROME(x) (((x) >> 15) & 0xff)
-
#define K8_NBEAL 0x50
#define K8_NBEAH 0x54
@@ -455,23 +412,6 @@ enum amd64_chipset_families {
F11_CPUS,
};
-/*
- * Structure to hold:
- *
- * 1) dynamically read status and error address HW registers
- * 2) sysfs entered values
- * 3) MCE values
- *
- * Depends on entry into the modules
- */
-struct amd64_error_info_regs {
- u32 nbcfg;
- u32 nbsh;
- u32 nbsl;
- u32 nbeah;
- u32 nbeal;
-};
-
/* Error injection control structure */
struct error_injection {
u32 section;
@@ -542,7 +482,7 @@ struct amd64_pvt {
u32 online_spare; /* On-Line spare Reg */
/* temp storage for when input is received from sysfs */
- struct amd64_error_info_regs ctl_error_info;
+ struct err_regs ctl_error_info;
/* place to store error injection parameters prior to issue */
struct error_injection injection;
@@ -601,11 +541,11 @@ struct low_ops {
int (*early_channel_count)(struct amd64_pvt *pvt);
u64 (*get_error_address)(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info);
+ struct err_regs *info);
void (*read_dram_base_limit)(struct amd64_pvt *pvt, int dram);
void (*read_dram_ctl_register)(struct amd64_pvt *pvt);
void (*map_sysaddr_to_csrow)(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info,
+ struct err_regs *info,
u64 SystemAddr);
int (*dbam_map_to_pages)(struct amd64_pvt *pvt, int dram_map);
};
@@ -637,8 +577,5 @@ static inline struct low_ops *family_ops(int index)
#define F10_MIN_SCRUB_RATE_BITS 0x5
#define F11_MIN_SCRUB_RATE_BITS 0x6
-int amd64_process_error_info(struct mem_ctl_info *mci,
- struct amd64_error_info_regs *info,
- int handle_errors);
int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
u64 *hole_offset, u64 *hole_size);
diff --git a/drivers/edac/amd64_edac_dbg.c b/drivers/edac/amd64_edac_dbg.c
index 0a41b248a4a..59cf2cf6e11 100644
--- a/drivers/edac/amd64_edac_dbg.c
+++ b/drivers/edac/amd64_edac_dbg.c
@@ -24,7 +24,7 @@ static ssize_t amd64_nbea_store(struct mem_ctl_info *mci, const char *data,
/* Process the Mapping request */
/* TODO: Add race prevention */
- amd64_process_error_info(mci, &pvt->ctl_error_info, 1);
+ amd_decode_nb_mce(pvt->mc_node_id, &pvt->ctl_error_info, 1);
return count;
}
diff --git a/drivers/edac/amd64_edac_err_types.c b/drivers/edac/amd64_edac_err_types.c
deleted file mode 100644
index f212ff12a9d..00000000000
--- a/drivers/edac/amd64_edac_err_types.c
+++ /dev/null
@@ -1,161 +0,0 @@
-#include "amd64_edac.h"
-
-/*
- * See F2x80 for K8 and F2x[1,0]80 for Fam10 and later. The table below is only
- * for DDR2 DRAM mapping.
- */
-u32 revf_quad_ddr2_shift[] = {
- 0, /* 0000b NULL DIMM (128mb) */
- 28, /* 0001b 256mb */
- 29, /* 0010b 512mb */
- 29, /* 0011b 512mb */
- 29, /* 0100b 512mb */
- 30, /* 0101b 1gb */
- 30, /* 0110b 1gb */
- 31, /* 0111b 2gb */
- 31, /* 1000b 2gb */
- 32, /* 1001b 4gb */
- 32, /* 1010b 4gb */
- 33, /* 1011b 8gb */
- 0, /* 1100b future */
- 0, /* 1101b future */
- 0, /* 1110b future */
- 0 /* 1111b future */
-};
-
-/*
- * Valid scrub rates for the K8 hardware memory scrubber. We map the scrubbing
- * bandwidth to a valid bit pattern. The 'set' operation finds the 'matching-
- * or higher value'.
- *
- *FIXME: Produce a better mapping/linearisation.
- */
-
-struct scrubrate scrubrates[] = {
- { 0x01, 1600000000UL},
- { 0x02, 800000000UL},
- { 0x03, 400000000UL},
- { 0x04, 200000000UL},
- { 0x05, 100000000UL},
- { 0x06, 50000000UL},
- { 0x07, 25000000UL},
- { 0x08, 12284069UL},
- { 0x09, 6274509UL},
- { 0x0A, 3121951UL},
- { 0x0B, 1560975UL},
- { 0x0C, 781440UL},
- { 0x0D, 390720UL},
- { 0x0E, 195300UL},
- { 0x0F, 97650UL},
- { 0x10, 48854UL},
- { 0x11, 24427UL},
- { 0x12, 12213UL},
- { 0x13, 6101UL},
- { 0x14, 3051UL},
- { 0x15, 1523UL},
- { 0x16, 761UL},
- { 0x00, 0UL}, /* scrubbing off */
-};
-
-/*
- * string representation for the different MCA reported error types, see F3x48
- * or MSR0000_0411.
- */
-const char *tt_msgs[] = { /* transaction type */
- "instruction",
- "data",
- "generic",
- "reserved"
-};
-
-const char *ll_msgs[] = { /* cache level */
- "L0",
- "L1",
- "L2",
- "L3/generic"
-};
-
-const char *rrrr_msgs[] = {
- "generic",
- "generic read",
- "generic write",
- "data read",
- "data write",
- "inst fetch",
- "prefetch",
- "evict",
- "snoop",
- "reserved RRRR= 9",
- "reserved RRRR= 10",
- "reserved RRRR= 11",
- "reserved RRRR= 12",
- "reserved RRRR= 13",
- "reserved RRRR= 14",
- "reserved RRRR= 15"
-};
-
-const char *pp_msgs[] = { /* participating processor */
- "local node originated (SRC)",
- "local node responded to request (RES)",
- "local node observed as 3rd party (OBS)",
- "generic"
-};
-
-const char *to_msgs[] = {
- "no timeout",
- "timed out"
-};
-
-const char *ii_msgs[] = { /* memory or i/o */
- "mem access",
- "reserved",
- "i/o access",
- "generic"
-};
-
-/* Map the 5 bits of Extended Error code to the string table. */
-const char *ext_msgs[] = { /* extended error */
- "K8 ECC error/F10 reserved", /* 0_0000b */
- "CRC error", /* 0_0001b */
- "sync error", /* 0_0010b */
- "mst abort", /* 0_0011b */
- "tgt abort", /* 0_0100b */
- "GART error", /* 0_0101b */
- "RMW error", /* 0_0110b */
- "Wdog timer error", /* 0_0111b */
- "F10-ECC/K8-Chipkill error", /* 0_1000b */
- "DEV Error", /* 0_1001b */
- "Link Data error", /* 0_1010b */
- "Link or L3 Protocol error", /* 0_1011b */
- "NB Array error", /* 0_1100b */
- "DRAM Parity error", /* 0_1101b */
- "Link Retry/GART Table Walk/DEV Table Walk error", /* 0_1110b */
- "Res 0x0ff error", /* 0_1111b */
- "Res 0x100 error", /* 1_0000b */
- "Res 0x101 error", /* 1_0001b */
- "Res 0x102 error", /* 1_0010b */
- "Res 0x103 error", /* 1_0011b */
- "Res 0x104 error", /* 1_0100b */
- "Res 0x105 error", /* 1_0101b */
- "Res 0x106 error", /* 1_0110b */
- "Res 0x107 error", /* 1_0111b */
- "Res 0x108 error", /* 1_1000b */
- "Res 0x109 error", /* 1_1001b */
- "Res 0x10A error", /* 1_1010b */
- "Res 0x10B error", /* 1_1011b */
- "L3 Cache Data error", /* 1_1100b */
- "L3 CacheTag error", /* 1_1101b */
- "L3 Cache LRU error", /* 1_1110b */
- "Res 0x1FF error" /* 1_1111b */
-};
-
-const char *htlink_msgs[] = {
- "none",
- "1",
- "2",
- "1 2",
- "3",
- "1 3",
- "2 3",
- "1 2 3"
-};
diff --git a/drivers/edac/edac_mce_amd.c b/drivers/edac/edac_mce_amd.c
new file mode 100644
index 00000000000..c8ca7136dac
--- /dev/null
+++ b/drivers/edac/edac_mce_amd.c
@@ -0,0 +1,422 @@
+#include <linux/module.h>
+#include "edac_mce_amd.h"
+
+static bool report_gart_errors;
+static void (*nb_bus_decoder)(int node_id, struct err_regs *regs);
+
+void amd_report_gart_errors(bool v)
+{
+ report_gart_errors = v;
+}
+EXPORT_SYMBOL_GPL(amd_report_gart_errors);
+
+void amd_register_ecc_decoder(void (*f)(int, struct err_regs *))
+{
+ nb_bus_decoder = f;
+}
+EXPORT_SYMBOL_GPL(amd_register_ecc_decoder);
+
+void amd_unregister_ecc_decoder(void (*f)(int, struct err_regs *))
+{
+ if (nb_bus_decoder) {
+ WARN_ON(nb_bus_decoder != f);
+
+ nb_bus_decoder = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(amd_unregister_ecc_decoder);
+
+/*
+ * string representation for the different MCA reported error types, see F3x48
+ * or MSR0000_0411.
+ */
+const char *tt_msgs[] = { /* transaction type */
+ "instruction",
+ "data",
+ "generic",
+ "reserved"
+};
+EXPORT_SYMBOL_GPL(tt_msgs);
+
+const char *ll_msgs[] = { /* cache level */
+ "L0",
+ "L1",
+ "L2",
+ "L3/generic"
+};
+EXPORT_SYMBOL_GPL(ll_msgs);
+
+const char *rrrr_msgs[] = {
+ "generic",
+ "generic read",
+ "generic write",
+ "data read",
+ "data write",
+ "inst fetch",
+ "prefetch",
+ "evict",
+ "snoop",
+ "reserved RRRR= 9",
+ "reserved RRRR= 10",
+ "reserved RRRR= 11",
+ "reserved RRRR= 12",
+ "reserved RRRR= 13",
+ "reserved RRRR= 14",
+ "reserved RRRR= 15"
+};
+EXPORT_SYMBOL_GPL(rrrr_msgs);
+
+const char *pp_msgs[] = { /* participating processor */
+ "local node originated (SRC)",
+ "local node responded to request (RES)",
+ "local node observed as 3rd party (OBS)",
+ "generic"
+};
+EXPORT_SYMBOL_GPL(pp_msgs);
+
+const char *to_msgs[] = {
+ "no timeout",
+ "timed out"
+};
+EXPORT_SYMBOL_GPL(to_msgs);
+
+const char *ii_msgs[] = { /* memory or i/o */
+ "mem access",
+ "reserved",
+ "i/o access",
+ "generic"
+};
+EXPORT_SYMBOL_GPL(ii_msgs);
+
+/*
+ * Map the 4 or 5 (family-specific) bits of Extended Error code to the
+ * string table.
+ */
+const char *ext_msgs[] = {
+ "K8 ECC error", /* 0_0000b */
+ "CRC error on link", /* 0_0001b */
+ "Sync error packets on link", /* 0_0010b */
+ "Master Abort during link operation", /* 0_0011b */
+ "Target Abort during link operation", /* 0_0100b */
+ "Invalid GART PTE entry during table walk", /* 0_0101b */
+ "Unsupported atomic RMW command received", /* 0_0110b */
+ "WDT error: NB transaction timeout", /* 0_0111b */
+ "ECC/ChipKill ECC error", /* 0_1000b */
+ "SVM DEV Error", /* 0_1001b */
+ "Link Data error", /* 0_1010b */
+ "Link/L3/Probe Filter Protocol error", /* 0_1011b */
+ "NB Internal Arrays Parity error", /* 0_1100b */
+ "DRAM Address/Control Parity error", /* 0_1101b */
+ "Link Transmission error", /* 0_1110b */
+ "GART/DEV Table Walk Data error" /* 0_1111b */
+ "Res 0x100 error", /* 1_0000b */
+ "Res 0x101 error", /* 1_0001b */
+ "Res 0x102 error", /* 1_0010b */
+ "Res 0x103 error", /* 1_0011b */
+ "Res 0x104 error", /* 1_0100b */
+ "Res 0x105 error", /* 1_0101b */
+ "Res 0x106 error", /* 1_0110b */
+ "Res 0x107 error", /* 1_0111b */
+ "Res 0x108 error", /* 1_1000b */
+ "Res 0x109 error", /* 1_1001b */
+ "Res 0x10A error", /* 1_1010b */
+ "Res 0x10B error", /* 1_1011b */
+ "ECC error in L3 Cache Data", /* 1_1100b */
+ "L3 Cache Tag error", /* 1_1101b */
+ "L3 Cache LRU Parity error", /* 1_1110b */
+ "Probe Filter error" /* 1_1111b */
+};
+EXPORT_SYMBOL_GPL(ext_msgs);
+
+static void amd_decode_dc_mce(u64 mc0_status)
+{
+ u32 ec = mc0_status & 0xffff;
+ u32 xec = (mc0_status >> 16) & 0xf;
+
+ pr_emerg(" Data Cache Error");
+
+ if (xec == 1 && TLB_ERROR(ec))
+ pr_cont(": %s TLB multimatch.\n", LL_MSG(ec));
+ else if (xec == 0) {
+ if (mc0_status & (1ULL << 40))
+ pr_cont(" during Data Scrub.\n");
+ else if (TLB_ERROR(ec))
+ pr_cont(": %s TLB parity error.\n", LL_MSG(ec));
+ else if (MEM_ERROR(ec)) {
+ u8 ll = ec & 0x3;
+ u8 tt = (ec >> 2) & 0x3;
+ u8 rrrr = (ec >> 4) & 0xf;
+
+ /* see F10h BKDG (31116), Table 92. */
+ if (ll == 0x1) {
+ if (tt != 0x1)
+ goto wrong_dc_mce;
+
+ pr_cont(": Data/Tag %s error.\n", RRRR_MSG(ec));
+
+ } else if (ll == 0x2 && rrrr == 0x3)
+ pr_cont(" during L1 linefill from L2.\n");
+ else
+ goto wrong_dc_mce;
+ } else if (BUS_ERROR(ec) && boot_cpu_data.x86 == 0xf)
+ pr_cont(" during system linefill.\n");
+ else
+ goto wrong_dc_mce;
+ } else
+ goto wrong_dc_mce;
+
+ return;
+
+wrong_dc_mce:
+ pr_warning("Corrupted DC MCE info?\n");
+}
+
+static void amd_decode_ic_mce(u64 mc1_status)
+{
+ u32 ec = mc1_status & 0xffff;
+ u32 xec = (mc1_status >> 16) & 0xf;
+
+ pr_emerg(" Instruction Cache Error");
+
+ if (xec == 1 && TLB_ERROR(ec))
+ pr_cont(": %s TLB multimatch.\n", LL_MSG(ec));
+ else if (xec == 0) {
+ if (TLB_ERROR(ec))
+ pr_cont(": %s TLB Parity error.\n", LL_MSG(ec));
+ else if (BUS_ERROR(ec)) {
+ if (boot_cpu_data.x86 == 0xf &&
+ (mc1_status & (1ULL << 58)))
+ pr_cont(" during system linefill.\n");
+ else
+ pr_cont(" during attempted NB data read.\n");
+ } else if (MEM_ERROR(ec)) {
+ u8 ll = ec & 0x3;
+ u8 rrrr = (ec >> 4) & 0xf;
+
+ if (ll == 0x2)
+ pr_cont(" during a linefill from L2.\n");
+ else if (ll == 0x1) {
+
+ switch (rrrr) {
+ case 0x5:
+ pr_cont(": Parity error during "
+ "data load.\n");
+ break;
+
+ case 0x7:
+ pr_cont(": Copyback Parity/Victim"
+ " error.\n");
+ break;
+
+ case 0x8:
+ pr_cont(": Tag Snoop error.\n");
+ break;
+
+ default:
+ goto wrong_ic_mce;
+ break;
+ }
+ }
+ } else
+ goto wrong_ic_mce;
+ } else
+ goto wrong_ic_mce;
+
+ return;
+
+wrong_ic_mce:
+ pr_warning("Corrupted IC MCE info?\n");
+}
+
+static void amd_decode_bu_mce(u64 mc2_status)
+{
+ u32 ec = mc2_status & 0xffff;
+ u32 xec = (mc2_status >> 16) & 0xf;
+
+ pr_emerg(" Bus Unit Error");
+
+ if (xec == 0x1)
+ pr_cont(" in the write data buffers.\n");
+ else if (xec == 0x3)
+ pr_cont(" in the victim data buffers.\n");
+ else if (xec == 0x2 && MEM_ERROR(ec))
+ pr_cont(": %s error in the L2 cache tags.\n", RRRR_MSG(ec));
+ else if (xec == 0x0) {
+ if (TLB_ERROR(ec))
+ pr_cont(": %s error in a Page Descriptor Cache or "
+ "Guest TLB.\n", TT_MSG(ec));
+ else if (BUS_ERROR(ec))
+ pr_cont(": %s/ECC error in data read from NB: %s.\n",
+ RRRR_MSG(ec), PP_MSG(ec));
+ else if (MEM_ERROR(ec)) {
+ u8 rrrr = (ec >> 4) & 0xf;
+
+ if (rrrr >= 0x7)
+ pr_cont(": %s error during data copyback.\n",
+ RRRR_MSG(ec));
+ else if (rrrr <= 0x1)
+ pr_cont(": %s parity/ECC error during data "
+ "access from L2.\n", RRRR_MSG(ec));
+ else
+ goto wrong_bu_mce;
+ } else
+ goto wrong_bu_mce;
+ } else
+ goto wrong_bu_mce;
+
+ return;
+
+wrong_bu_mce:
+ pr_warning("Corrupted BU MCE info?\n");
+}
+
+static void amd_decode_ls_mce(u64 mc3_status)
+{
+ u32 ec = mc3_status & 0xffff;
+ u32 xec = (mc3_status >> 16) & 0xf;
+
+ pr_emerg(" Load Store Error");
+
+ if (xec == 0x0) {
+ u8 rrrr = (ec >> 4) & 0xf;
+
+ if (!BUS_ERROR(ec) || (rrrr != 0x3 && rrrr != 0x4))
+ goto wrong_ls_mce;
+
+ pr_cont(" during %s.\n", RRRR_MSG(ec));
+ }
+ return;
+
+wrong_ls_mce:
+ pr_warning("Corrupted LS MCE info?\n");
+}
+
+void amd_decode_nb_mce(int node_id, struct err_regs *regs, int handle_errors)
+{
+ u32 ec = ERROR_CODE(regs->nbsl);
+ u32 xec = EXT_ERROR_CODE(regs->nbsl);
+
+ if (!handle_errors)
+ return;
+
+ pr_emerg(" Northbridge Error, node %d", node_id);
+
+ /*
+ * F10h, revD can disable ErrCpu[3:0] so check that first and also the
+ * value encoding has changed so interpret those differently
+ */
+ if ((boot_cpu_data.x86 == 0x10) &&
+ (boot_cpu_data.x86_model > 8)) {
+ if (regs->nbsh & K8_NBSH_ERR_CPU_VAL)
+ pr_cont(", core: %u\n", (u8)(regs->nbsh & 0xf));
+ } else {
+ pr_cont(", core: %d\n", ilog2((regs->nbsh & 0xf)));
+ }
+
+
+ pr_emerg("%s.\n", EXT_ERR_MSG(xec));
+
+ if (BUS_ERROR(ec) && nb_bus_decoder)
+ nb_bus_decoder(node_id, regs);
+}
+EXPORT_SYMBOL_GPL(amd_decode_nb_mce);
+
+static void amd_decode_fr_mce(u64 mc5_status)
+{
+ /* we have only one error signature so match all fields at once. */
+ if ((mc5_status & 0xffff) == 0x0f0f)
+ pr_emerg(" FR Error: CPU Watchdog timer expire.\n");
+ else
+ pr_warning("Corrupted FR MCE info?\n");
+}
+
+static inline void amd_decode_err_code(unsigned int ec)
+{
+ if (TLB_ERROR(ec)) {
+ /*
+ * GART errors are intended to help graphics driver developers
+ * to detect bad GART PTEs. It is recommended by AMD to disable
+ * GART table walk error reporting by default[1] (currently
+ * being disabled in mce_cpu_quirks()) and according to the
+ * comment in mce_cpu_quirks(), such GART errors can be
+ * incorrectly triggered. We may see these errors anyway and
+ * unless requested by the user, they won't be reported.
+ *
+ * [1] section 13.10.1 on BIOS and Kernel Developers Guide for
+ * AMD NPT family 0Fh processors
+ */
+ if (!report_gart_errors)
+ return;
+
+ pr_emerg(" Transaction: %s, Cache Level %s\n",
+ TT_MSG(ec), LL_MSG(ec));
+ } else if (MEM_ERROR(ec)) {
+ pr_emerg(" Transaction: %s, Type: %s, Cache Level: %s",
+ RRRR_MSG(ec), TT_MSG(ec), LL_MSG(ec));
+ } else if (BUS_ERROR(ec)) {
+ pr_emerg(" Transaction type: %s(%s), %s, Cache Level: %s, "
+ "Participating Processor: %s\n",
+ RRRR_MSG(ec), II_MSG(ec), TO_MSG(ec), LL_MSG(ec),
+ PP_MSG(ec));
+ } else
+ pr_warning("Huh? Unknown MCE error 0x%x\n", ec);
+}
+
+void decode_mce(struct mce *m)
+{
+ struct err_regs regs;
+ int node, ecc;
+
+ pr_emerg("MC%d_STATUS: ", m->bank);
+
+ pr_cont("%sorrected error, report: %s, MiscV: %svalid, "
+ "CPU context corrupt: %s",
+ ((m->status & MCI_STATUS_UC) ? "Unc" : "C"),
+ ((m->status & MCI_STATUS_EN) ? "yes" : "no"),
+ ((m->status & MCI_STATUS_MISCV) ? "" : "in"),
+ ((m->status & MCI_STATUS_PCC) ? "yes" : "no"));
+
+ /* do the two bits[14:13] together */
+ ecc = m->status & (3ULL << 45);
+ if (ecc)
+ pr_cont(", %sECC Error", ((ecc == 2) ? "C" : "U"));
+
+ pr_cont("\n");
+
+ switch (m->bank) {
+ case 0:
+ amd_decode_dc_mce(m->status);
+ break;
+
+ case 1:
+ amd_decode_ic_mce(m->status);
+ break;
+
+ case 2:
+ amd_decode_bu_mce(m->status);
+ break;
+
+ case 3:
+ amd_decode_ls_mce(m->status);
+ break;
+
+ case 4:
+ regs.nbsl = (u32) m->status;
+ regs.nbsh = (u32)(m->status >> 32);
+ regs.nbeal = (u32) m->addr;
+ regs.nbeah = (u32)(m->addr >> 32);
+ node = per_cpu(cpu_llc_id, m->extcpu);
+
+ amd_decode_nb_mce(node, &regs, 1);
+ break;
+
+ case 5:
+ amd_decode_fr_mce(m->status);
+ break;
+
+ default:
+ break;
+ }
+
+ amd_decode_err_code(m->status & 0xffff);
+}
diff --git a/drivers/edac/edac_mce_amd.h b/drivers/edac/edac_mce_amd.h
new file mode 100644
index 00000000000..df23ee065f7
--- /dev/null
+++ b/drivers/edac/edac_mce_amd.h
@@ -0,0 +1,69 @@
+#ifndef _EDAC_MCE_AMD_H
+#define _EDAC_MCE_AMD_H
+
+#include <asm/mce.h>
+
+#define ERROR_CODE(x) ((x) & 0xffff)
+#define EXT_ERROR_CODE(x) (((x) >> 16) & 0x1f)
+#define EXT_ERR_MSG(x) ext_msgs[EXT_ERROR_CODE(x)]
+
+#define LOW_SYNDROME(x) (((x) >> 15) & 0xff)
+#define HIGH_SYNDROME(x) (((x) >> 24) & 0xff)
+
+#define TLB_ERROR(x) (((x) & 0xFFF0) == 0x0010)
+#define MEM_ERROR(x) (((x) & 0xFF00) == 0x0100)
+#define BUS_ERROR(x) (((x) & 0xF800) == 0x0800)
+
+#define TT(x) (((x) >> 2) & 0x3)
+#define TT_MSG(x) tt_msgs[TT(x)]
+#define II(x) (((x) >> 2) & 0x3)
+#define II_MSG(x) ii_msgs[II(x)]
+#define LL(x) (((x) >> 0) & 0x3)
+#define LL_MSG(x) ll_msgs[LL(x)]
+#define RRRR(x) (((x) >> 4) & 0xf)
+#define RRRR_MSG(x) rrrr_msgs[RRRR(x)]
+#define TO(x) (((x) >> 8) & 0x1)
+#define TO_MSG(x) to_msgs[TO(x)]
+#define PP(x) (((x) >> 9) & 0x3)
+#define PP_MSG(x) pp_msgs[PP(x)]
+
+#define K8_NBSH 0x4C
+
+#define K8_NBSH_VALID_BIT BIT(31)
+#define K8_NBSH_OVERFLOW BIT(30)
+#define K8_NBSH_UC_ERR BIT(29)
+#define K8_NBSH_ERR_EN BIT(28)
+#define K8_NBSH_MISCV BIT(27)
+#define K8_NBSH_VALID_ERROR_ADDR BIT(26)
+#define K8_NBSH_PCC BIT(25)
+#define K8_NBSH_ERR_CPU_VAL BIT(24)
+#define K8_NBSH_CECC BIT(14)
+#define K8_NBSH_UECC BIT(13)
+#define K8_NBSH_ERR_SCRUBER BIT(8)
+
+extern const char *tt_msgs[];
+extern const char *ll_msgs[];
+extern const char *rrrr_msgs[];
+extern const char *pp_msgs[];
+extern const char *to_msgs[];
+extern const char *ii_msgs[];
+extern const char *ext_msgs[];
+
+/*
+ * relevant NB regs
+ */
+struct err_regs {
+ u32 nbcfg;
+ u32 nbsh;
+ u32 nbsl;
+ u32 nbeah;
+ u32 nbeal;
+};
+
+
+void amd_report_gart_errors(bool);
+void amd_register_ecc_decoder(void (*f)(int, struct err_regs *));
+void amd_unregister_ecc_decoder(void (*f)(int, struct err_regs *));
+void amd_decode_nb_mce(int, struct err_regs *, int);
+
+#endif /* _EDAC_MCE_AMD_H */
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7831a0318d3..111afbe8de0 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -31,21 +31,6 @@ config HID
If unsure, say Y.
-config HID_DEBUG
- bool "HID debugging support"
- default y
- depends on HID
- ---help---
- This option lets the HID layer output diagnostics about its internal
- state, resolve HID usages, dump HID fields, etc. Individual HID drivers
- use this debugging facility to output information about individual HID
- devices, etc.
-
- This feature is useful for those who are either debugging the HID parser
- or any HID hardware device.
-
- If unsure, say Y.
-
config HIDRAW
bool "/dev/hidraw raw HID device support"
depends on HID
@@ -152,6 +137,13 @@ config HID_GYRATION
---help---
Support for Gyration remote control.
+config HID_TWINHAN
+ tristate "Twinhan" if EMBEDDED
+ depends on USB_HID
+ default !EMBEDDED
+ ---help---
+ Support for Twinhan IR remote control.
+
config HID_KENSINGTON
tristate "Kensington" if EMBEDDED
depends on USB_HID
@@ -176,6 +168,7 @@ config LOGITECH_FF
- Logitech WingMan Cordless RumblePad 2
- Logitech WingMan Force 3D
- Logitech Formula Force EX
+ - Logitech WingMan Formula Force GP
- Logitech MOMO Force wheel
and if you want to enable force feedback for them.
@@ -314,9 +307,9 @@ config THRUSTMASTER_FF
depends on HID_THRUSTMASTER
select INPUT_FF_MEMLESS
---help---
- Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or
- a THRUSTMASTER Ferrari GT Rumble Force or Force Feedback Wheel and
- want to enable force feedback support for it.
+ Say Y here if you have a THRUSTMASTER FireStore Dual Power 2 or 3,
+ a THRUSTMASTER Dual Trigger 3-in-1 or a THRUSTMASTER Ferrari GT
+ Rumble Force or Force Feedback Wheel.
config HID_WACOM
tristate "Wacom Bluetooth devices support" if EMBEDDED
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index db35151673b..0de2dff5542 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -3,9 +3,12 @@
#
hid-objs := hid-core.o hid-input.o
+ifdef CONFIG_DEBUG_FS
+ hid-objs += hid-debug.o
+endif
+
obj-$(CONFIG_HID) += hid.o
-hid-$(CONFIG_HID_DEBUG) += hid-debug.o
hid-$(CONFIG_HIDRAW) += hidraw.o
hid-logitech-objs := hid-lg.o
@@ -40,6 +43,7 @@ obj-$(CONFIG_HID_SUNPLUS) += hid-sunplus.o
obj-$(CONFIG_HID_GREENASIA) += hid-gaff.o
obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o
obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
+obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
obj-$(CONFIG_HID_WACOM) += hid-wacom.o
diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c
index 42ea359e94c..df474c699fb 100644
--- a/drivers/hid/hid-a4tech.c
+++ b/drivers/hid/hid-a4tech.c
@@ -145,12 +145,12 @@ static struct hid_driver a4_driver = {
.remove = a4_remove,
};
-static int a4_init(void)
+static int __init a4_init(void)
{
return hid_register_driver(&a4_driver);
}
-static void a4_exit(void)
+static void __exit a4_exit(void)
{
hid_unregister_driver(&a4_driver);
}
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 303ccce05bb..4b96e7a898c 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -451,7 +451,7 @@ static struct hid_driver apple_driver = {
.input_mapped = apple_input_mapped,
};
-static int apple_init(void)
+static int __init apple_init(void)
{
int ret;
@@ -462,7 +462,7 @@ static int apple_init(void)
return ret;
}
-static void apple_exit(void)
+static void __exit apple_exit(void)
{
hid_unregister_driver(&apple_driver);
}
diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c
index 2f6723133a4..4ce7aa3a519 100644
--- a/drivers/hid/hid-belkin.c
+++ b/drivers/hid/hid-belkin.c
@@ -88,12 +88,12 @@ static struct hid_driver belkin_driver = {
.probe = belkin_probe,
};
-static int belkin_init(void)
+static int __init belkin_init(void)
{
return hid_register_driver(&belkin_driver);
}
-static void belkin_exit(void)
+static void __exit belkin_exit(void)
{
hid_unregister_driver(&belkin_driver);
}
diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c
index ab8209e7e45..7e597d7f770 100644
--- a/drivers/hid/hid-cherry.c
+++ b/drivers/hid/hid-cherry.c
@@ -70,12 +70,12 @@ static struct hid_driver ch_driver = {
.input_mapping = ch_input_mapping,
};
-static int ch_init(void)
+static int __init ch_init(void)
{
return hid_register_driver(&ch_driver);
}
-static void ch_exit(void)
+static void __exit ch_exit(void)
{
hid_unregister_driver(&ch_driver);
}
diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c
index 7f91076d849..8965ad93d51 100644
--- a/drivers/hid/hid-chicony.c
+++ b/drivers/hid/hid-chicony.c
@@ -63,12 +63,12 @@ static struct hid_driver ch_driver = {
.input_mapping = ch_input_mapping,
};
-static int ch_init(void)
+static int __init ch_init(void)
{
return hid_register_driver(&ch_driver);
}
-static void ch_exit(void)
+static void __exit ch_exit(void)
{
hid_unregister_driver(&ch_driver);
}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 5eb10c2ce66..342b7d36d7b 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -44,12 +44,10 @@
#define DRIVER_DESC "HID core driver"
#define DRIVER_LICENSE "GPL"
-#ifdef CONFIG_HID_DEBUG
int hid_debug = 0;
module_param_named(debug, hid_debug, int, 0600);
-MODULE_PARM_DESC(debug, "HID debugging (0=off, 1=probing info, 2=continuous data dumping)");
+MODULE_PARM_DESC(debug, "toggle HID debugging messages");
EXPORT_SYMBOL_GPL(hid_debug);
-#endif
/*
* Register a new report for a device.
@@ -861,7 +859,7 @@ static void hid_process_event(struct hid_device *hid, struct hid_field *field,
struct hid_driver *hdrv = hid->driver;
int ret;
- hid_dump_input(usage, value);
+ hid_dump_input(hid, usage, value);
if (hdrv && hdrv->event && hid_match_usage(hid, usage)) {
ret = hdrv->event(hid, field, usage, value);
@@ -983,11 +981,10 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
{
unsigned size = field->report_size;
- hid_dump_input(field->usage + offset, value);
+ hid_dump_input(field->report->device, field->usage + offset, value);
if (offset >= field->report_count) {
dbg_hid("offset (%d) exceeds report_count (%d)\n", offset, field->report_count);
- hid_dump_field(field, 8);
return -1;
}
if (field->logical_minimum < 0) {
@@ -1078,6 +1075,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
struct hid_report_enum *report_enum;
struct hid_driver *hdrv;
struct hid_report *report;
+ char *buf;
unsigned int i;
int ret;
@@ -1091,18 +1089,38 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i
return -1;
}
- dbg_hid("report (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
+ buf = kmalloc(sizeof(char) * HID_DEBUG_BUFSIZE,
+ interrupt ? GFP_ATOMIC : GFP_KERNEL);
+
+ if (!buf) {
+ report = hid_get_report(report_enum, data);
+ goto nomem;
+ }
+
+ snprintf(buf, HID_DEBUG_BUFSIZE - 1,
+ "\nreport (size %u) (%snumbered)\n", size, report_enum->numbered ? "" : "un");
+ hid_debug_event(hid, buf);
report = hid_get_report(report_enum, data);
- if (!report)
+ if (!report) {
+ kfree(buf);
return -1;
+ }
/* dump the report */
- dbg_hid("report %d (size %u) = ", report->id, size);
- for (i = 0; i < size; i++)
- dbg_hid_line(" %02x", data[i]);
- dbg_hid_line("\n");
+ snprintf(buf, HID_DEBUG_BUFSIZE - 1,
+ "report %d (size %u) = ", report->id, size);
+ hid_debug_event(hid, buf);
+ for (i = 0; i < size; i++) {
+ snprintf(buf, HID_DEBUG_BUFSIZE - 1,
+ " %02x", data[i]);
+ hid_debug_event(hid, buf);
+ }
+ hid_debug_event(hid, "\n");
+
+ kfree(buf);
+nomem:
if (hdrv && hdrv->raw_event && hid_match_report(hid, report)) {
ret = hdrv->raw_event(hid, report, data, size);
if (ret != 0)
@@ -1292,6 +1310,7 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_F3D) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_FORCE3D_PRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_MOMO_WHEEL2) },
@@ -1311,15 +1330,17 @@ static const struct hid_device_id hid_blacklist[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb324) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb654) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TOPSEED, USB_DEVICE_ID_TOPSEED_CYBERLINK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0005) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ZEROPLUS, 0x0030) },
- { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, 0x030c) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_PRESENTER_8K_BT) },
{ }
};
@@ -1622,12 +1643,8 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0002) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0003) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD2) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD3) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD4) },
- { HID_USB_DEVICE(USB_VENDOR_ID_SOUNDGRAPH, USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD5) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY1) },
{ HID_USB_DEVICE(USB_VENDOR_ID_TENX, USB_DEVICE_ID_TENX_IBUDDY2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
@@ -1694,6 +1711,11 @@ static bool hid_ignore(struct hid_device *hdev)
hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST)
return true;
break;
+ case USB_VENDOR_ID_SOUNDGRAPH:
+ if (hdev->product >= USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST &&
+ hdev->product <= USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST)
+ return true;
+ break;
}
if (hdev->type == HID_TYPE_USBMOUSE &&
@@ -1725,6 +1747,8 @@ int hid_add_device(struct hid_device *hdev)
if (!ret)
hdev->status |= HID_STAT_ADDED;
+ hid_debug_register(hdev, dev_name(&hdev->dev));
+
return ret;
}
EXPORT_SYMBOL_GPL(hid_add_device);
@@ -1761,6 +1785,9 @@ struct hid_device *hid_allocate_device(void)
for (i = 0; i < HID_REPORT_TYPES; i++)
INIT_LIST_HEAD(&hdev->report_enum[i].report_list);
+ init_waitqueue_head(&hdev->debug_wait);
+ INIT_LIST_HEAD(&hdev->debug_list);
+
return hdev;
err:
put_device(&hdev->dev);
@@ -1772,6 +1799,7 @@ static void hid_remove_device(struct hid_device *hdev)
{
if (hdev->status & HID_STAT_ADDED) {
device_del(&hdev->dev);
+ hid_debug_unregister(hdev);
hdev->status &= ~HID_STAT_ADDED;
}
}
@@ -1847,6 +1875,10 @@ static int __init hid_init(void)
{
int ret;
+ if (hid_debug)
+ printk(KERN_WARNING "HID: hid_debug is now used solely for parser and driver debugging.\n"
+ "HID: debugfs is now used for inspecting the device (report descriptor, reports)\n");
+
ret = bus_register(&hid_bus_type);
if (ret) {
printk(KERN_ERR "HID: can't register hid bus\n");
@@ -1857,6 +1889,8 @@ static int __init hid_init(void)
if (ret)
goto err_bus;
+ hid_debug_init();
+
return 0;
err_bus:
bus_unregister(&hid_bus_type);
@@ -1866,6 +1900,7 @@ err:
static void __exit hid_exit(void)
{
+ hid_debug_exit();
hidraw_exit();
bus_unregister(&hid_bus_type);
}
diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c
index 9d6d3b91773..62e9cb10e88 100644
--- a/drivers/hid/hid-cypress.c
+++ b/drivers/hid/hid-cypress.c
@@ -141,12 +141,12 @@ static struct hid_driver cp_driver = {
.probe = cp_probe,
};
-static int cp_init(void)
+static int __init cp_init(void)
{
return hid_register_driver(&cp_driver);
}
-static void cp_exit(void)
+static void __exit cp_exit(void)
{
hid_unregister_driver(&cp_driver);
}
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 04359ed64b8..6abd0369aed 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -1,9 +1,9 @@
/*
* (c) 1999 Andreas Gal <gal@cs.uni-magdeburg.de>
* (c) 2000-2001 Vojtech Pavlik <vojtech@ucw.cz>
- * (c) 2007 Jiri Kosina
+ * (c) 2007-2009 Jiri Kosina
*
- * Some debug stuff for the HID parser.
+ * HID debugging support
*/
/*
@@ -26,9 +26,17 @@
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/poll.h>
+
#include <linux/hid.h>
#include <linux/hid-debug.h>
+static struct dentry *hid_debug_root;
+
struct hid_usage_entry {
unsigned page;
unsigned usage;
@@ -339,72 +347,120 @@ static const struct hid_usage_entry hid_usage_table[] = {
{ 0, 0, NULL }
};
-static void resolv_usage_page(unsigned page) {
+/* Either output directly into simple seq_file, or (if f == NULL)
+ * allocate a separate buffer that will then be passed to the 'events'
+ * ringbuffer.
+ *
+ * This is because these functions can be called both for "one-shot"
+ * "rdesc" while resolving, or for blocking "events".
+ *
+ * This holds both for resolv_usage_page() and hid_resolv_usage().
+ */
+static char *resolv_usage_page(unsigned page, struct seq_file *f) {
const struct hid_usage_entry *p;
+ char *buf = NULL;
+
+ if (!f) {
+ buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_ATOMIC);
+ if (!buf)
+ return ERR_PTR(-ENOMEM);
+ }
for (p = hid_usage_table; p->description; p++)
if (p->page == page) {
- printk("%s", p->description);
- return;
+ if (!f) {
+ snprintf(buf, HID_DEBUG_BUFSIZE, "%s",
+ p->description);
+ return buf;
+ }
+ else {
+ seq_printf(f, "%s", p->description);
+ return NULL;
+ }
}
- printk("%04x", page);
+ if (!f)
+ snprintf(buf, HID_DEBUG_BUFSIZE, "%04x", page);
+ else
+ seq_printf(f, "%04x", page);
+ return buf;
}
-void hid_resolv_usage(unsigned usage) {
+char *hid_resolv_usage(unsigned usage, struct seq_file *f) {
const struct hid_usage_entry *p;
+ char *buf = NULL;
+ int len = 0;
+
+ buf = resolv_usage_page(usage >> 16, f);
+ if (IS_ERR(buf)) {
+ printk(KERN_ERR "error allocating HID debug buffer\n");
+ return NULL;
+ }
- if (!hid_debug)
- return;
- resolv_usage_page(usage >> 16);
- printk(".");
+ if (!f) {
+ len = strlen(buf);
+ snprintf(buf+len, max(0, HID_DEBUG_BUFSIZE - len), ".");
+ len++;
+ }
+ else {
+ seq_printf(f, ".");
+ }
for (p = hid_usage_table; p->description; p++)
if (p->page == (usage >> 16)) {
for(++p; p->description && p->usage != 0; p++)
if (p->usage == (usage & 0xffff)) {
- printk("%s", p->description);
- return;
+ if (!f)
+ snprintf(buf + len,
+ max(0,HID_DEBUG_BUFSIZE - len - 1),
+ "%s", p->description);
+ else
+ seq_printf(f,
+ "%s",
+ p->description);
+ return buf;
}
break;
}
- printk("%04x", usage & 0xffff);
+ if (!f)
+ snprintf(buf + len, max(0, HID_DEBUG_BUFSIZE - len - 1),
+ "%04x", usage & 0xffff);
+ else
+ seq_printf(f, "%04x", usage & 0xffff);
+ return buf;
}
EXPORT_SYMBOL_GPL(hid_resolv_usage);
-static void tab(int n) {
- printk(KERN_DEBUG "%*s", n, "");
+static void tab(int n, struct seq_file *f) {
+ seq_printf(f, "%*s", n, "");
}
-void hid_dump_field(struct hid_field *field, int n) {
+void hid_dump_field(struct hid_field *field, int n, struct seq_file *f) {
int j;
- if (!hid_debug)
- return;
-
if (field->physical) {
- tab(n);
- printk("Physical(");
- hid_resolv_usage(field->physical); printk(")\n");
+ tab(n, f);
+ seq_printf(f, "Physical(");
+ hid_resolv_usage(field->physical, f); seq_printf(f, ")\n");
}
if (field->logical) {
- tab(n);
- printk("Logical(");
- hid_resolv_usage(field->logical); printk(")\n");
+ tab(n, f);
+ seq_printf(f, "Logical(");
+ hid_resolv_usage(field->logical, f); seq_printf(f, ")\n");
}
- tab(n); printk("Usage(%d)\n", field->maxusage);
+ tab(n, f); seq_printf(f, "Usage(%d)\n", field->maxusage);
for (j = 0; j < field->maxusage; j++) {
- tab(n+2); hid_resolv_usage(field->usage[j].hid); printk("\n");
+ tab(n+2, f); hid_resolv_usage(field->usage[j].hid, f); seq_printf(f, "\n");
}
if (field->logical_minimum != field->logical_maximum) {
- tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum);
- tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum);
+ tab(n, f); seq_printf(f, "Logical Minimum(%d)\n", field->logical_minimum);
+ tab(n, f); seq_printf(f, "Logical Maximum(%d)\n", field->logical_maximum);
}
if (field->physical_minimum != field->physical_maximum) {
- tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum);
- tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum);
+ tab(n, f); seq_printf(f, "Physical Minimum(%d)\n", field->physical_minimum);
+ tab(n, f); seq_printf(f, "Physical Maximum(%d)\n", field->physical_maximum);
}
if (field->unit_exponent) {
- tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent);
+ tab(n, f); seq_printf(f, "Unit Exponent(%d)\n", field->unit_exponent);
}
if (field->unit) {
static const char *systems[5] = { "None", "SI Linear", "SI Rotation", "English Linear", "English Rotation" };
@@ -425,77 +481,75 @@ void hid_dump_field(struct hid_field *field, int n) {
data >>= 4;
if(sys > 4) {
- tab(n); printk("Unit(Invalid)\n");
+ tab(n, f); seq_printf(f, "Unit(Invalid)\n");
}
else {
int earlier_unit = 0;
- tab(n); printk("Unit(%s : ", systems[sys]);
+ tab(n, f); seq_printf(f, "Unit(%s : ", systems[sys]);
for (i=1 ; i<sizeof(__u32)*2 ; i++) {
char nibble = data & 0xf;
data >>= 4;
if (nibble != 0) {
if(earlier_unit++ > 0)
- printk("*");
- printk("%s", units[sys][i]);
+ seq_printf(f, "*");
+ seq_printf(f, "%s", units[sys][i]);
if(nibble != 1) {
/* This is a _signed_ nibble(!) */
int val = nibble & 0x7;
if(nibble & 0x08)
val = -((0x7 & ~val) +1);
- printk("^%d", val);
+ seq_printf(f, "^%d", val);
}
}
}
- printk(")\n");
+ seq_printf(f, ")\n");
}
}
- tab(n); printk("Report Size(%u)\n", field->report_size);
- tab(n); printk("Report Count(%u)\n", field->report_count);
- tab(n); printk("Report Offset(%u)\n", field->report_offset);
+ tab(n, f); seq_printf(f, "Report Size(%u)\n", field->report_size);
+ tab(n, f); seq_printf(f, "Report Count(%u)\n", field->report_count);
+ tab(n, f); seq_printf(f, "Report Offset(%u)\n", field->report_offset);
- tab(n); printk("Flags( ");
+ tab(n, f); seq_printf(f, "Flags( ");
j = field->flags;
- printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : "");
- printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array ");
- printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute ");
- printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : "");
- printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : "");
- printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : "");
- printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : "");
- printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : "");
- printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : "");
- printk(")\n");
+ seq_printf(f, "%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : "");
+ seq_printf(f, "%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array ");
+ seq_printf(f, "%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute ");
+ seq_printf(f, "%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : "");
+ seq_printf(f, "%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : "");
+ seq_printf(f, "%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPreferredState " : "");
+ seq_printf(f, "%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : "");
+ seq_printf(f, "%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : "");
+ seq_printf(f, "%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : "");
+ seq_printf(f, ")\n");
}
EXPORT_SYMBOL_GPL(hid_dump_field);
-void hid_dump_device(struct hid_device *device) {
+void hid_dump_device(struct hid_device *device, struct seq_file *f)
+{
struct hid_report_enum *report_enum;
struct hid_report *report;
struct list_head *list;
unsigned i,k;
static const char *table[] = {"INPUT", "OUTPUT", "FEATURE"};
- if (!hid_debug)
- return;
-
for (i = 0; i < HID_REPORT_TYPES; i++) {
report_enum = device->report_enum + i;
list = report_enum->report_list.next;
while (list != &report_enum->report_list) {
report = (struct hid_report *) list;
- tab(2);
- printk("%s", table[i]);
+ tab(2, f);
+ seq_printf(f, "%s", table[i]);
if (report->id)
- printk("(%d)", report->id);
- printk("[%s]", table[report->type]);
- printk("\n");
+ seq_printf(f, "(%d)", report->id);
+ seq_printf(f, "[%s]", table[report->type]);
+ seq_printf(f, "\n");
for (k = 0; k < report->maxfield; k++) {
- tab(4);
- printk("Field(%d)\n", k);
- hid_dump_field(report->field[k], 6);
+ tab(4, f);
+ seq_printf(f, "Field(%d)\n", k);
+ hid_dump_field(report->field[k], 6, f);
}
list = list->next;
}
@@ -503,13 +557,37 @@ void hid_dump_device(struct hid_device *device) {
}
EXPORT_SYMBOL_GPL(hid_dump_device);
-void hid_dump_input(struct hid_usage *usage, __s32 value) {
- if (hid_debug < 2)
+/* enqueue string to 'events' ring buffer */
+void hid_debug_event(struct hid_device *hdev, char *buf)
+{
+ int i;
+ struct hid_debug_list *list;
+
+ list_for_each_entry(list, &hdev->debug_list, node) {
+ for (i = 0; i <= strlen(buf); i++)
+ list->hid_debug_buf[(list->tail + i) % (HID_DEBUG_BUFSIZE - 1)] =
+ buf[i];
+ list->tail = (list->tail + i) % (HID_DEBUG_BUFSIZE - 1);
+ }
+}
+EXPORT_SYMBOL_GPL(hid_debug_event);
+
+void hid_dump_input(struct hid_device *hdev, struct hid_usage *usage, __s32 value)
+{
+ char *buf;
+ int len;
+
+ buf = hid_resolv_usage(usage->hid, NULL);
+ if (!buf)
return;
+ len = strlen(buf);
+ snprintf(buf + len, HID_DEBUG_BUFSIZE - len - 1, " = %d\n", value);
+
+ hid_debug_event(hdev, buf);
+
+ kfree(buf);
+ wake_up_interruptible(&hdev->debug_wait);
- printk(KERN_DEBUG "hid-debug: input ");
- hid_resolv_usage(usage->hid);
- printk(" = %d\n", value);
}
EXPORT_SYMBOL_GPL(hid_dump_input);
@@ -786,12 +864,221 @@ static const char **names[EV_MAX + 1] = {
[EV_SND] = sounds, [EV_REP] = repeats,
};
-void hid_resolv_event(__u8 type, __u16 code) {
+void hid_resolv_event(__u8 type, __u16 code, struct seq_file *f) {
- if (!hid_debug)
- return;
-
- printk("%s.%s", events[type] ? events[type] : "?",
+ seq_printf(f, "%s.%s", events[type] ? events[type] : "?",
names[type] ? (names[type][code] ? names[type][code] : "?") : "?");
}
-EXPORT_SYMBOL_GPL(hid_resolv_event);
+
+void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f)
+{
+ int i, j, k;
+ struct hid_report *report;
+ struct hid_usage *usage;
+
+ for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
+ list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
+ for (i = 0; i < report->maxfield; i++) {
+ for ( j = 0; j < report->field[i]->maxusage; j++) {
+ usage = report->field[i]->usage + j;
+ hid_resolv_usage(usage->hid, f);
+ seq_printf(f, " ---> ");
+ hid_resolv_event(usage->type, usage->code, f);
+ seq_printf(f, "\n");
+ }
+ }
+ }
+ }
+
+}
+
+
+static int hid_debug_rdesc_show(struct seq_file *f, void *p)
+{
+ struct hid_device *hdev = f->private;
+ int i;
+
+ /* dump HID report descriptor */
+ for (i = 0; i < hdev->rsize; i++)
+ seq_printf(f, "%02x ", hdev->rdesc[i]);
+ seq_printf(f, "\n\n");
+
+ /* dump parsed data and input mappings */
+ hid_dump_device(hdev, f);
+ seq_printf(f, "\n");
+ hid_dump_input_mapping(hdev, f);
+
+ return 0;
+}
+
+static int hid_debug_rdesc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hid_debug_rdesc_show, inode->i_private);
+}
+
+static int hid_debug_events_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+ struct hid_debug_list *list;
+
+ if (!(list = kzalloc(sizeof(struct hid_debug_list), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (!(list->hid_debug_buf = kzalloc(sizeof(char) * HID_DEBUG_BUFSIZE, GFP_KERNEL))) {
+ err = -ENOMEM;
+ kfree(list);
+ goto out;
+ }
+ list->hdev = (struct hid_device *) inode->i_private;
+ file->private_data = list;
+ mutex_init(&list->read_mutex);
+
+ list_add_tail(&list->node, &list->hdev->debug_list);
+
+out:
+ return err;
+}
+
+static ssize_t hid_debug_events_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct hid_debug_list *list = file->private_data;
+ int ret = 0, len;
+ DECLARE_WAITQUEUE(wait, current);
+
+ while (ret == 0) {
+ mutex_lock(&list->read_mutex);
+ if (list->head == list->tail) {
+ add_wait_queue(&list->hdev->debug_wait, &wait);
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ while (list->head == list->tail) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ break;
+ }
+ if (signal_pending(current)) {
+ ret = -ERESTARTSYS;
+ break;
+ }
+
+ if (!list->hdev || !list->hdev->debug) {
+ ret = -EIO;
+ break;
+ }
+
+ /* allow O_NONBLOCK from other threads */
+ mutex_unlock(&list->read_mutex);
+ schedule();
+ mutex_lock(&list->read_mutex);
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&list->hdev->debug_wait, &wait);
+ }
+
+ if (ret)
+ goto out;
+
+ /* pass the ringbuffer contents to userspace */
+copy_rest:
+ if (list->tail == list->head)
+ goto out;
+ if (list->tail > list->head) {
+ len = list->tail - list->head;
+
+ if (copy_to_user(buffer + ret, &list->hid_debug_buf[list->head], len)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ ret += len;
+ list->head += len;
+ } else {
+ len = HID_DEBUG_BUFSIZE - list->head;
+
+ if (copy_to_user(buffer, &list->hid_debug_buf[list->head], len)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ list->head = 0;
+ ret += len;
+ goto copy_rest;
+ }
+
+ }
+out:
+ mutex_unlock(&list->read_mutex);
+ return ret;
+}
+
+static unsigned int hid_debug_events_poll(struct file *file, poll_table *wait)
+{
+ struct hid_debug_list *list = file->private_data;
+
+ poll_wait(file, &list->hdev->debug_wait, wait);
+ if (list->head != list->tail)
+ return POLLIN | POLLRDNORM;
+ if (!list->hdev->debug)
+ return POLLERR | POLLHUP;
+ return 0;
+}
+
+static int hid_debug_events_release(struct inode *inode, struct file *file)
+{
+ struct hid_debug_list *list = file->private_data;
+
+ list_del(&list->node);
+ kfree(list->hid_debug_buf);
+ kfree(list);
+
+ return 0;
+}
+
+static const struct file_operations hid_debug_rdesc_fops = {
+ .open = hid_debug_rdesc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations hid_debug_events_fops = {
+ .owner = THIS_MODULE,
+ .open = hid_debug_events_open,
+ .read = hid_debug_events_read,
+ .poll = hid_debug_events_poll,
+ .release = hid_debug_events_release,
+};
+
+
+void hid_debug_register(struct hid_device *hdev, const char *name)
+{
+ hdev->debug_dir = debugfs_create_dir(name, hid_debug_root);
+ hdev->debug_rdesc = debugfs_create_file("rdesc", 0400,
+ hdev->debug_dir, hdev, &hid_debug_rdesc_fops);
+ hdev->debug_events = debugfs_create_file("events", 0400,
+ hdev->debug_dir, hdev, &hid_debug_events_fops);
+ hdev->debug = 1;
+}
+
+void hid_debug_unregister(struct hid_device *hdev)
+{
+ hdev->debug = 0;
+ wake_up_interruptible(&hdev->debug_wait);
+ debugfs_remove(hdev->debug_rdesc);
+ debugfs_remove(hdev->debug_events);
+ debugfs_remove(hdev->debug_dir);
+}
+
+void hid_debug_init(void)
+{
+ hid_debug_root = debugfs_create_dir("hid", NULL);
+}
+
+void hid_debug_exit(void)
+{
+ debugfs_remove_recursive(hid_debug_root);
+}
+
diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c
index 0a1fe054799..ca1163e9d42 100644
--- a/drivers/hid/hid-ezkey.c
+++ b/drivers/hid/hid-ezkey.c
@@ -78,12 +78,12 @@ static struct hid_driver ez_driver = {
.event = ez_event,
};
-static int ez_init(void)
+static int __init ez_init(void)
{
return hid_register_driver(&ez_driver);
}
-static void ez_exit(void)
+static void __exit ez_exit(void)
{
hid_unregister_driver(&ez_driver);
}
diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c
index d42d222097a..cab13e8c7d2 100644
--- a/drivers/hid/hid-gyration.c
+++ b/drivers/hid/hid-gyration.c
@@ -81,12 +81,12 @@ static struct hid_driver gyration_driver = {
.event = gyration_event,
};
-static int gyration_init(void)
+static int __init gyration_init(void)
{
return hid_register_driver(&gyration_driver);
}
-static void gyration_exit(void)
+static void __exit gyration_exit(void)
{
hid_unregister_driver(&gyration_driver);
}
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 63010103792..adbef5d069c 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -296,6 +296,7 @@
#define USB_DEVICE_ID_LOGITECH_WINGMAN_F3D 0xc283
#define USB_DEVICE_ID_LOGITECH_FORCE3D_PRO 0xc286
#define USB_DEVICE_ID_LOGITECH_WHEEL 0xc294
+#define USB_DEVICE_ID_LOGITECH_WINGMAN_FFG 0xc293
#define USB_DEVICE_ID_LOGITECH_MOMO_WHEEL 0xc295
#define USB_DEVICE_ID_LOGITECH_G25_WHEEL 0xc299
#define USB_DEVICE_ID_LOGITECH_ELITE_KBD 0xc30a
@@ -359,6 +360,9 @@
#define USB_VENDOR_ID_PETALYNX 0x18b1
#define USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE 0x0037
+#define USB_VENDOR_ID_PHILIPS 0x0471
+#define USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE 0x0617
+
#define USB_VENDOR_ID_PLAYDOTCOM 0x0b43
#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003
@@ -376,11 +380,8 @@
#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268
#define USB_VENDOR_ID_SOUNDGRAPH 0x15c2
-#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD 0x0038
-#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD2 0x0036
-#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD3 0x0034
-#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD4 0x0044
-#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LCD5 0x0045
+#define USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST 0x0034
+#define USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST 0x0046
#define USB_VENDOR_ID_SUN 0x0430
#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab
@@ -403,6 +404,9 @@
#define USB_VENDOR_ID_TURBOX 0x062a
#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201
+#define USB_VENDOR_ID_TWINHAN 0x6253
+#define USB_DEVICE_ID_TWINHAN_IR_REMOTE 0x0100
+
#define USB_VENDOR_ID_UCLOGIC 0x5543
#define USB_DEVICE_ID_UCLOGIC_TABLET_PF1209 0x0042
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 7f183b7147e..5862b0f3b55 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -159,17 +159,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
field->hidinput = hidinput;
- dbg_hid("Mapping: ");
- hid_resolv_usage(usage->hid);
- dbg_hid_line(" ---> ");
-
if (field->flags & HID_MAIN_ITEM_CONSTANT)
goto ignore;
/* only LED usages are supported in output fields */
if (field->report_type == HID_OUTPUT_REPORT &&
(usage->hid & HID_USAGE_PAGE) != HID_UP_LED) {
- dbg_hid_line(" [non-LED output field] ");
goto ignore;
}
@@ -561,15 +556,9 @@ mapped:
set_bit(MSC_SCAN, input->mscbit);
}
- hid_resolv_event(usage->type, usage->code);
-
- dbg_hid_line("\n");
-
- return;
-
ignore:
- dbg_hid_line("IGNORED\n");
return;
+
}
void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct hid_usage *usage, __s32 value)
diff --git a/drivers/hid/hid-kensington.c b/drivers/hid/hid-kensington.c
index 7353bd79cbe..a5b4016e9bd 100644
--- a/drivers/hid/hid-kensington.c
+++ b/drivers/hid/hid-kensington.c
@@ -48,12 +48,12 @@ static struct hid_driver ks_driver = {
.input_mapping = ks_input_mapping,
};
-static int ks_init(void)
+static int __init ks_init(void)
{
return hid_register_driver(&ks_driver);
}
-static void ks_exit(void)
+static void __exit ks_exit(void)
{
hid_unregister_driver(&ks_driver);
}
diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c
index 72ee3fec56d..f8871712b7b 100644
--- a/drivers/hid/hid-kye.c
+++ b/drivers/hid/hid-kye.c
@@ -54,12 +54,12 @@ static struct hid_driver kye_driver = {
.report_fixup = kye_report_fixup,
};
-static int kye_init(void)
+static int __init kye_init(void)
{
return hid_register_driver(&kye_driver);
}
-static void kye_exit(void)
+static void __exit kye_exit(void)
{
hid_unregister_driver(&kye_driver);
}
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index 7afbaa0efd1..0f870a3243e 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -299,6 +299,8 @@ static const struct hid_device_id lg_devices[] = {
.driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_G25_WHEEL),
.driver_data = LG_FF },
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
+ .driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
.driver_data = LG_FF2 },
{ }
@@ -315,12 +317,12 @@ static struct hid_driver lg_driver = {
.probe = lg_probe,
};
-static int lg_init(void)
+static int __init lg_init(void)
{
return hid_register_driver(&lg_driver);
}
-static void lg_exit(void)
+static void __exit lg_exit(void)
{
hid_unregister_driver(&lg_driver);
}
diff --git a/drivers/hid/hid-lgff.c b/drivers/hid/hid-lgff.c
index 56099709581..987abebe082 100644
--- a/drivers/hid/hid-lgff.c
+++ b/drivers/hid/hid-lgff.c
@@ -67,6 +67,7 @@ static const struct dev_type devices[] = {
{ 0x046d, 0xc219, ff_rumble },
{ 0x046d, 0xc283, ff_joystick },
{ 0x046d, 0xc286, ff_joystick_ac },
+ { 0x046d, 0xc293, ff_joystick },
{ 0x046d, 0xc294, ff_wheel },
{ 0x046d, 0xc295, ff_joystick },
{ 0x046d, 0xca03, ff_wheel },
@@ -150,11 +151,6 @@ int lgff_init(struct hid_device* hid)
/* Check that the report looks ok */
report = list_entry(report_list->next, struct hid_report, list);
- if (!report) {
- err_hid("NULL output report");
- return -1;
- }
-
field = report->field[0];
if (!field) {
err_hid("NULL field");
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index 5e9e37a0506..359cc447c6c 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -197,12 +197,12 @@ static struct hid_driver ms_driver = {
.probe = ms_probe,
};
-static int ms_init(void)
+static int __init ms_init(void)
{
return hid_register_driver(&ms_driver);
}
-static void ms_exit(void)
+static void __exit ms_exit(void)
{
hid_unregister_driver(&ms_driver);
}
diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c
index 240f87618be..2cd05aa244b 100644
--- a/drivers/hid/hid-monterey.c
+++ b/drivers/hid/hid-monterey.c
@@ -65,12 +65,12 @@ static struct hid_driver mr_driver = {
.input_mapping = mr_input_mapping,
};
-static int mr_init(void)
+static int __init mr_init(void)
{
return hid_register_driver(&mr_driver);
}
-static void mr_exit(void)
+static void __exit mr_exit(void)
{
hid_unregister_driver(&mr_driver);
}
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index 75ed9d2c1a3..49ce69d7bba 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -27,6 +27,9 @@
struct ntrig_data {
__s32 x, y, id, w, h;
char reading_a_point, found_contact_id;
+ char pen_active;
+ char finger_active;
+ char inverted;
};
/*
@@ -63,10 +66,7 @@ static int ntrig_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_UP_DIGITIZER:
switch (usage->hid) {
/* we do not want to map these for now */
- case HID_DG_INVERT: /* value is always 0 */
- case HID_DG_ERASER: /* value is always 0 */
case HID_DG_CONTACTID: /* value is useless */
- case HID_DG_BARRELSWITCH: /* doubtful */
case HID_DG_INPUTMODE:
case HID_DG_DEVICEINDEX:
case HID_DG_CONTACTCOUNT:
@@ -125,6 +125,18 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
if (hid->claimed & HID_CLAIMED_INPUT) {
switch (usage->hid) {
+
+ case HID_DG_INRANGE:
+ if (field->application & 0x3)
+ nd->pen_active = (value != 0);
+ else
+ nd->finger_active = (value != 0);
+ return 0;
+
+ case HID_DG_INVERT:
+ nd->inverted = value;
+ return 0;
+
case HID_GD_X:
nd->x = value;
nd->reading_a_point = 1;
@@ -147,7 +159,11 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
* report received in a finger event. We want
* to emit a normal (X, Y) position
*/
- if (! nd->found_contact_id) {
+ if (!nd->found_contact_id) {
+ if (nd->pen_active && nd->finger_active) {
+ input_report_key(input, BTN_TOOL_DOUBLETAP, 0);
+ input_report_key(input, BTN_TOOL_DOUBLETAP, 1);
+ }
input_event(input, EV_ABS, ABS_X, nd->x);
input_event(input, EV_ABS, ABS_Y, nd->y);
}
@@ -159,6 +175,14 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
* to emit a normal (X, Y) position
*/
if (! nd->found_contact_id) {
+ if (nd->pen_active && nd->finger_active) {
+ input_report_key(input,
+ nd->inverted ? BTN_TOOL_RUBBER : BTN_TOOL_PEN
+ , 0);
+ input_report_key(input,
+ nd->inverted ? BTN_TOOL_RUBBER : BTN_TOOL_PEN
+ , 1);
+ }
input_event(input, EV_ABS, ABS_X, nd->x);
input_event(input, EV_ABS, ABS_Y, nd->y);
input_event(input, EV_ABS, ABS_PRESSURE, value);
@@ -233,6 +257,7 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret)
kfree (nd);
+
return ret;
}
@@ -265,12 +290,12 @@ static struct hid_driver ntrig_driver = {
.event = ntrig_event,
};
-static int ntrig_init(void)
+static int __init ntrig_init(void)
{
return hid_register_driver(&ntrig_driver);
}
-static void ntrig_exit(void)
+static void __exit ntrig_exit(void)
{
hid_unregister_driver(&ntrig_driver);
}
diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c
index 2e83e8ff891..500fbd0652d 100644
--- a/drivers/hid/hid-petalynx.c
+++ b/drivers/hid/hid-petalynx.c
@@ -105,12 +105,12 @@ static struct hid_driver pl_driver = {
.probe = pl_probe,
};
-static int pl_init(void)
+static int __init pl_init(void)
{
return hid_register_driver(&pl_driver);
}
-static void pl_exit(void)
+static void __exit pl_exit(void)
{
hid_unregister_driver(&pl_driver);
}
diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c
index 4db9a348376..c6d7dbc935b 100644
--- a/drivers/hid/hid-pl.c
+++ b/drivers/hid/hid-pl.c
@@ -217,12 +217,12 @@ static struct hid_driver pl_driver = {
.probe = pl_probe,
};
-static int pl_init(void)
+static int __init pl_init(void)
{
return hid_register_driver(&pl_driver);
}
-static void pl_exit(void)
+static void __exit pl_exit(void)
{
hid_unregister_driver(&pl_driver);
}
diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c
index 07083aa6c19..5b222eed069 100644
--- a/drivers/hid/hid-samsung.c
+++ b/drivers/hid/hid-samsung.c
@@ -25,25 +25,48 @@
/*
* Samsung IrDA remote controller (reports as Cypress USB Mouse).
*
+ * There are several variants for 0419:0001:
+ *
+ * 1. 184 byte report descriptor
* Vendor specific report #4 has a size of 48 bit,
* and therefore is not accepted when inspecting the descriptors.
* As a workaround we reinterpret the report as:
* Variable type, count 6, size 8 bit, log. maximum 255
* The burden to reconstruct the data is moved into user space.
+ *
+ * 2. 203 byte report descriptor
+ * Report #4 has an array field with logical range 0..18 instead of 1..15.
+ *
+ * 3. 135 byte report descriptor
+ * Report #4 has an array field with logical range 0..17 instead of 1..14.
*/
static void samsung_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int rsize)
{
- if (rsize >= 182 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
+ if (rsize == 184 && rdesc[175] == 0x25 && rdesc[176] == 0x40 &&
rdesc[177] == 0x75 && rdesc[178] == 0x30 &&
rdesc[179] == 0x95 && rdesc[180] == 0x01 &&
rdesc[182] == 0x40) {
- dev_info(&hdev->dev, "fixing up Samsung IrDA report "
- "descriptor\n");
+ dev_info(&hdev->dev, "fixing up Samsung IrDA %d byte report "
+ "descriptor\n", 184);
rdesc[176] = 0xff;
rdesc[178] = 0x08;
rdesc[180] = 0x06;
rdesc[182] = 0x42;
+ } else
+ if (rsize == 203 && rdesc[192] == 0x15 && rdesc[193] == 0x0 &&
+ rdesc[194] == 0x25 && rdesc[195] == 0x12) {
+ dev_info(&hdev->dev, "fixing up Samsung IrDA %d byte report "
+ "descriptor\n", 203);
+ rdesc[193] = 0x1;
+ rdesc[195] = 0xf;
+ } else
+ if (rsize == 135 && rdesc[124] == 0x15 && rdesc[125] == 0x0 &&
+ rdesc[126] == 0x25 && rdesc[127] == 0x11) {
+ dev_info(&hdev->dev, "fixing up Samsung IrDA %d byte report "
+ "descriptor\n", 135);
+ rdesc[125] = 0x1;
+ rdesc[127] = 0xe;
}
}
@@ -51,6 +74,7 @@ static int samsung_probe(struct hid_device *hdev,
const struct hid_device_id *id)
{
int ret;
+ unsigned int cmask = HID_CONNECT_DEFAULT;
ret = hid_parse(hdev);
if (ret) {
@@ -58,8 +82,13 @@ static int samsung_probe(struct hid_device *hdev,
goto err_free;
}
- ret = hid_hw_start(hdev, (HID_CONNECT_DEFAULT & ~HID_CONNECT_HIDINPUT) |
- HID_CONNECT_HIDDEV_FORCE);
+ if (hdev->rsize == 184) {
+ /* disable hidinput, force hiddev */
+ cmask = (cmask & ~HID_CONNECT_HIDINPUT) |
+ HID_CONNECT_HIDDEV_FORCE;
+ }
+
+ ret = hid_hw_start(hdev, cmask);
if (ret) {
dev_err(&hdev->dev, "hw start failed\n");
goto err_free;
@@ -83,12 +112,12 @@ static struct hid_driver samsung_driver = {
.probe = samsung_probe,
};
-static int samsung_init(void)
+static int __init samsung_init(void)
{
return hid_register_driver(&samsung_driver);
}
-static void samsung_exit(void)
+static void __exit samsung_exit(void)
{
hid_unregister_driver(&samsung_driver);
}
diff --git a/drivers/hid/hid-sjoy.c b/drivers/hid/hid-sjoy.c
index eab169e5c37..203c438b016 100644
--- a/drivers/hid/hid-sjoy.c
+++ b/drivers/hid/hid-sjoy.c
@@ -163,12 +163,12 @@ static struct hid_driver sjoy_driver = {
.probe = sjoy_probe,
};
-static int sjoy_init(void)
+static int __init sjoy_init(void)
{
return hid_register_driver(&sjoy_driver);
}
-static void sjoy_exit(void)
+static void __exit sjoy_exit(void)
{
hid_unregister_driver(&sjoy_driver);
}
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index c2599388a35..4e8450228a2 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -135,12 +135,12 @@ static struct hid_driver sony_driver = {
.report_fixup = sony_report_fixup,
};
-static int sony_init(void)
+static int __init sony_init(void)
{
return hid_register_driver(&sony_driver);
}
-static void sony_exit(void)
+static void __exit sony_exit(void)
{
hid_unregister_driver(&sony_driver);
}
diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c
index e0a8fd36a85..438107d9f1b 100644
--- a/drivers/hid/hid-sunplus.c
+++ b/drivers/hid/hid-sunplus.c
@@ -65,12 +65,12 @@ static struct hid_driver sp_driver = {
.input_mapping = sp_input_mapping,
};
-static int sp_init(void)
+static int __init sp_init(void)
{
return hid_register_driver(&sp_driver);
}
-static void sp_exit(void)
+static void __exit sp_exit(void)
{
hid_unregister_driver(&sp_driver);
}
diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c
index fcd6ccd02fe..167ea746fb9 100644
--- a/drivers/hid/hid-tmff.c
+++ b/drivers/hid/hid-tmff.c
@@ -243,7 +243,11 @@ err:
static const struct hid_device_id tm_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300),
.driver_data = (unsigned long)ff_rumble },
- { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304),
+ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304), /* FireStorm Dual Power 2 (and 3) */
+ .driver_data = (unsigned long)ff_rumble },
+ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323), /* Dual Trigger 3-in-1 (PC Mode) */
+ .driver_data = (unsigned long)ff_rumble },
+ { HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb324), /* Dual Trigger 3-in-1 (PS3 Mode) */
.driver_data = (unsigned long)ff_rumble },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb651), /* FGT Rumble Force Wheel */
.driver_data = (unsigned long)ff_rumble },
@@ -259,12 +263,12 @@ static struct hid_driver tm_driver = {
.probe = tm_probe,
};
-static int tm_init(void)
+static int __init tm_init(void)
{
return hid_register_driver(&tm_driver);
}
-static void tm_exit(void)
+static void __exit tm_exit(void)
{
hid_unregister_driver(&tm_driver);
}
diff --git a/drivers/hid/hid-topseed.c b/drivers/hid/hid-topseed.c
index 152ccfabeba..6925eda1081 100644
--- a/drivers/hid/hid-topseed.c
+++ b/drivers/hid/hid-topseed.c
@@ -60,12 +60,12 @@ static struct hid_driver ts_driver = {
.input_mapping = ts_input_mapping,
};
-static int ts_init(void)
+static int __init ts_init(void)
{
return hid_register_driver(&ts_driver);
}
-static void ts_exit(void)
+static void __exit ts_exit(void)
{
hid_unregister_driver(&ts_driver);
}
diff --git a/drivers/hid/hid-twinhan.c b/drivers/hid/hid-twinhan.c
new file mode 100644
index 00000000000..b05f602c051
--- /dev/null
+++ b/drivers/hid/hid-twinhan.c
@@ -0,0 +1,147 @@
+/*
+ * HID driver for TwinHan IR remote control
+ *
+ * Based on hid-gyration.c
+ *
+ * Copyright (c) 2009 Bruno Prémont <bonbons@linux-vserver.org>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License.
+ */
+
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/* Remote control key layout + listing:
+ *
+ * Full Screen Power
+ * KEY_SCREEN KEY_POWER2
+ *
+ * 1 2 3
+ * KEY_NUMERIC_1 KEY_NUMERIC_2 KEY_NUMERIC_3
+ *
+ * 4 5 6
+ * KEY_NUMERIC_4 KEY_NUMERIC_5 KEY_NUMERIC_6
+ *
+ * 7 8 9
+ * KEY_NUMERIC_7 KEY_NUMERIC_8 KEY_NUMERIC_9
+ *
+ * REC 0 Favorite
+ * KEY_RECORD KEY_NUMERIC_0 KEY_FAVORITES
+ *
+ * Rewind Forward
+ * KEY_REWIND CH+ KEY_FORWARD
+ * KEY_CHANNELUP
+ *
+ * VOL- > VOL+
+ * KEY_VOLUMEDOWN KEY_PLAY KEY_VOLUMEUP
+ *
+ * CH-
+ * KEY_CHANNELDOWN
+ * Recall Stop
+ * KEY_RESTART KEY_STOP
+ *
+ * Timeshift/Pause Mute Cancel
+ * KEY_PAUSE KEY_MUTE KEY_CANCEL
+ *
+ * Capture Preview EPG
+ * KEY_PRINT KEY_PROGRAM KEY_EPG
+ *
+ * Record List Tab Teletext
+ * KEY_LIST KEY_TAB KEY_TEXT
+ */
+
+#define th_map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, \
+ EV_KEY, (c))
+static int twinhan_input_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD)
+ return 0;
+
+ switch (usage->hid & HID_USAGE) {
+ /* Map all keys from Twinhan Remote */
+ case 0x004: th_map_key_clear(KEY_TEXT); break;
+ case 0x006: th_map_key_clear(KEY_RESTART); break;
+ case 0x008: th_map_key_clear(KEY_EPG); break;
+ case 0x00c: th_map_key_clear(KEY_REWIND); break;
+ case 0x00e: th_map_key_clear(KEY_PROGRAM); break;
+ case 0x00f: th_map_key_clear(KEY_LIST); break;
+ case 0x010: th_map_key_clear(KEY_MUTE); break;
+ case 0x011: th_map_key_clear(KEY_FORWARD); break;
+ case 0x013: th_map_key_clear(KEY_PRINT); break;
+ case 0x017: th_map_key_clear(KEY_PAUSE); break;
+ case 0x019: th_map_key_clear(KEY_FAVORITES); break;
+ case 0x01d: th_map_key_clear(KEY_SCREEN); break;
+ case 0x01e: th_map_key_clear(KEY_NUMERIC_1); break;
+ case 0x01f: th_map_key_clear(KEY_NUMERIC_2); break;
+ case 0x020: th_map_key_clear(KEY_NUMERIC_3); break;
+ case 0x021: th_map_key_clear(KEY_NUMERIC_4); break;
+ case 0x022: th_map_key_clear(KEY_NUMERIC_5); break;
+ case 0x023: th_map_key_clear(KEY_NUMERIC_6); break;
+ case 0x024: th_map_key_clear(KEY_NUMERIC_7); break;
+ case 0x025: th_map_key_clear(KEY_NUMERIC_8); break;
+ case 0x026: th_map_key_clear(KEY_NUMERIC_9); break;
+ case 0x027: th_map_key_clear(KEY_NUMERIC_0); break;
+ case 0x028: th_map_key_clear(KEY_PLAY); break;
+ case 0x029: th_map_key_clear(KEY_CANCEL); break;
+ case 0x02b: th_map_key_clear(KEY_TAB); break;
+ /* Power = 0x0e0 + 0x0e1 + 0x0e2 + 0x03f */
+ case 0x03f: th_map_key_clear(KEY_POWER2); break;
+ case 0x04a: th_map_key_clear(KEY_RECORD); break;
+ case 0x04b: th_map_key_clear(KEY_CHANNELUP); break;
+ case 0x04d: th_map_key_clear(KEY_STOP); break;
+ case 0x04e: th_map_key_clear(KEY_CHANNELDOWN); break;
+ /* Volume down = 0x0e1 + 0x051 */
+ case 0x051: th_map_key_clear(KEY_VOLUMEDOWN); break;
+ /* Volume up = 0x0e1 + 0x052 */
+ case 0x052: th_map_key_clear(KEY_VOLUMEUP); break;
+ /* Kill the extra keys used for multi-key "power" and "volume" keys
+ * as well as continuously to release CTRL,ALT,META,... keys */
+ case 0x0e0:
+ case 0x0e1:
+ case 0x0e2:
+ case 0x0e3:
+ case 0x0e4:
+ case 0x0e5:
+ case 0x0e6:
+ case 0x0e7:
+ default:
+ return -1;
+ }
+ return 1;
+}
+
+static const struct hid_device_id twinhan_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_TWINHAN, USB_DEVICE_ID_TWINHAN_IR_REMOTE) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, twinhan_devices);
+
+static struct hid_driver twinhan_driver = {
+ .name = "twinhan",
+ .id_table = twinhan_devices,
+ .input_mapping = twinhan_input_mapping,
+};
+
+static int twinhan_init(void)
+{
+ return hid_register_driver(&twinhan_driver);
+}
+
+static void twinhan_exit(void)
+{
+ hid_unregister_driver(&twinhan_driver);
+}
+
+module_init(twinhan_init);
+module_exit(twinhan_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
index 1f9237f511e..74754217224 100644
--- a/drivers/hid/hid-wacom.c
+++ b/drivers/hid/hid-wacom.c
@@ -237,7 +237,7 @@ static struct hid_driver wacom_driver = {
.raw_event = wacom_raw_event,
};
-static int wacom_init(void)
+static int __init wacom_init(void)
{
int ret;
@@ -248,7 +248,7 @@ static int wacom_init(void)
return ret;
}
-static void wacom_exit(void)
+static void __exit wacom_exit(void)
{
hid_unregister_driver(&wacom_driver);
}
diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c
index 57f710757bf..a79f0d78c6b 100644
--- a/drivers/hid/hid-zpff.c
+++ b/drivers/hid/hid-zpff.c
@@ -152,12 +152,12 @@ static struct hid_driver zp_driver = {
.probe = zp_probe,
};
-static int zp_init(void)
+static int __init zp_init(void)
{
return hid_register_driver(&zp_driver);
}
-static void zp_exit(void)
+static void __exit zp_exit(void)
{
hid_unregister_driver(&zp_driver);
}
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index 3c1fcb7640a..1b0e07a67d6 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -4,8 +4,8 @@
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- * Copyright (c) 2006-2008 Jiri Kosina
* Copyright (c) 2007-2008 Oliver Neukum
+ * Copyright (c) 2006-2009 Jiri Kosina
*/
/*
@@ -489,7 +489,8 @@ static void hid_ctrl(struct urb *urb)
wake_up(&usbhid->wait);
}
-void __usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir)
+static void __usbhid_submit_report(struct hid_device *hid, struct hid_report *report,
+ unsigned char dir)
{
int head;
struct usbhid_device *usbhid = hid->driver_data;
@@ -885,11 +886,6 @@ static int usbhid_parse(struct hid_device *hid)
goto err;
}
- dbg_hid("report descriptor (size %u, read %d) = ", rsize, n);
- for (n = 0; n < rsize; n++)
- dbg_hid_line(" %02x", (unsigned char) rdesc[n]);
- dbg_hid_line("\n");
-
ret = hid_parse_report(hid, rdesc, rsize);
kfree(rdesc);
if (ret) {
@@ -986,7 +982,6 @@ static int usbhid_start(struct hid_device *hid)
setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
spin_lock_init(&usbhid->lock);
- spin_lock_init(&usbhid->lock);
usbhid->intf = intf;
usbhid->ifnum = interface->desc.bInterfaceNumber;
@@ -1004,7 +999,6 @@ static int usbhid_start(struct hid_device *hid)
usbhid->urbctrl->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP);
usbhid_init_reports(hid);
- hid_dump_device(hid);
set_bit(HID_STARTED, &usbhid->iofl);
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index d8f7423f363..0d9045aa2c4 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -201,7 +201,7 @@ int usbhid_quirks_init(char **quirks_param)
u32 quirks;
int n = 0, m;
- for (; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) {
+ for (; n < MAX_USBHID_BOOT_QUIRKS && quirks_param[n]; n++) {
m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x",
&idVendor, &idProduct, &quirks);
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index 215b2addddb..4d1dc0cf140 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -44,7 +44,7 @@
#define HIDDEV_MINOR_BASE 96
#define HIDDEV_MINORS 16
#endif
-#define HIDDEV_BUFFER_SIZE 64
+#define HIDDEV_BUFFER_SIZE 2048
struct hiddev {
int exist;
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 2d5016691d4..2e25b7a827d 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -702,6 +702,23 @@ config SENSORS_SHT15
This driver can also be built as a module. If so, the module
will be called sht15.
+config SENSORS_S3C
+ tristate "S3C24XX/S3C64XX Inbuilt ADC"
+ depends on ARCH_S3C2410 || ARCH_S3C64XX
+ help
+ If you say yes here you get support for the on-board ADCs of
+ the Samsung S3C24XX or S3C64XX series of SoC
+
+ This driver can also be built as a module. If so, the module
+ will be called s3c-hwmo.
+
+config SENSORS_S3C_RAW
+ bool "Include raw channel attributes in sysfs"
+ depends on SENSORS_S3C
+ help
+ Say Y here if you want to include raw copies of all the ADC
+ channels in sysfs.
+
config SENSORS_SIS5595
tristate "Silicon Integrated Systems Corp. SiS5595"
depends on PCI
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b793dce6bed..7f239a247c3 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -76,6 +76,7 @@ obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
+obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o
obj-$(CONFIG_SENSORS_SHT15) += sht15.o
obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c
new file mode 100644
index 00000000000..3a524f2fe49
--- /dev/null
+++ b/drivers/hwmon/s3c-hwmon.c
@@ -0,0 +1,405 @@
+/* linux/drivers/hwmon/s3c-hwmon.c
+ *
+ * Copyright (C) 2005, 2008, 2009 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * S3C24XX/S3C64XX ADC hwmon support
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+
+#include <plat/adc.h>
+#include <plat/hwmon.h>
+
+struct s3c_hwmon_attr {
+ struct sensor_device_attribute in;
+ struct sensor_device_attribute label;
+ char in_name[12];
+ char label_name[12];
+};
+
+/**
+ * struct s3c_hwmon - ADC hwmon client information
+ * @lock: Access lock to serialise the conversions.
+ * @client: The client we registered with the S3C ADC core.
+ * @hwmon_dev: The hwmon device we created.
+ * @attr: The holders for the channel attributes.
+*/
+struct s3c_hwmon {
+ struct semaphore lock;
+ struct s3c_adc_client *client;
+ struct device *hwmon_dev;
+
+ struct s3c_hwmon_attr attrs[8];
+};
+
+/**
+ * s3c_hwmon_read_ch - read a value from a given adc channel.
+ * @dev: The device.
+ * @hwmon: Our state.
+ * @channel: The channel we're reading from.
+ *
+ * Read a value from the @channel with the proper locking and sleep until
+ * either the read completes or we timeout awaiting the ADC core to get
+ * back to us.
+ */
+static int s3c_hwmon_read_ch(struct device *dev,
+ struct s3c_hwmon *hwmon, int channel)
+{
+ int ret;
+
+ ret = down_interruptible(&hwmon->lock);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "reading channel %d\n", channel);
+
+ ret = s3c_adc_read(hwmon->client, channel);
+ up(&hwmon->lock);
+
+ return ret;
+}
+
+#ifdef CONFIG_SENSORS_S3C_RAW
+/**
+ * s3c_hwmon_show_raw - show a conversion from the raw channel number.
+ * @dev: The device that the attribute belongs to.
+ * @attr: The attribute being read.
+ * @buf: The result buffer.
+ *
+ * This show deals with the raw attribute, registered for each possible
+ * ADC channel. This does a conversion and returns the raw (un-scaled)
+ * value returned from the hardware.
+ */
+static ssize_t s3c_hwmon_show_raw(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct s3c_hwmon *adc = platform_get_drvdata(to_platform_device(dev));
+ struct sensor_device_attribute *sa = to_sensor_dev_attr(attr);
+ int ret;
+
+ ret = s3c_hwmon_read_ch(dev, adc, sa->index);
+
+ return (ret < 0) ? ret : snprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+#define DEF_ADC_ATTR(x) \
+ static SENSOR_DEVICE_ATTR(adc##x##_raw, S_IRUGO, s3c_hwmon_show_raw, NULL, x)
+
+DEF_ADC_ATTR(0);
+DEF_ADC_ATTR(1);
+DEF_ADC_ATTR(2);
+DEF_ADC_ATTR(3);
+DEF_ADC_ATTR(4);
+DEF_ADC_ATTR(5);
+DEF_ADC_ATTR(6);
+DEF_ADC_ATTR(7);
+
+static struct attribute *s3c_hwmon_attrs[9] = {
+ &sensor_dev_attr_adc0_raw.dev_attr.attr,
+ &sensor_dev_attr_adc1_raw.dev_attr.attr,
+ &sensor_dev_attr_adc2_raw.dev_attr.attr,
+ &sensor_dev_attr_adc3_raw.dev_attr.attr,
+ &sensor_dev_attr_adc4_raw.dev_attr.attr,
+ &sensor_dev_attr_adc5_raw.dev_attr.attr,
+ &sensor_dev_attr_adc6_raw.dev_attr.attr,
+ &sensor_dev_attr_adc7_raw.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute_group s3c_hwmon_attrgroup = {
+ .attrs = s3c_hwmon_attrs,
+};
+
+static inline int s3c_hwmon_add_raw(struct device *dev)
+{
+ return sysfs_create_group(&dev->kobj, &s3c_hwmon_attrgroup);
+}
+
+static inline void s3c_hwmon_remove_raw(struct device *dev)
+{
+ sysfs_remove_group(&dev->kobj, &s3c_hwmon_attrgroup);
+}
+
+#else
+
+static inline int s3c_hwmon_add_raw(struct device *dev) { return 0; }
+static inline void s3c_hwmon_remove_raw(struct device *dev) { }
+
+#endif /* CONFIG_SENSORS_S3C_RAW */
+
+/**
+ * s3c_hwmon_ch_show - show value of a given channel
+ * @dev: The device that the attribute belongs to.
+ * @attr: The attribute being read.
+ * @buf: The result buffer.
+ *
+ * Read a value from the ADC and scale it before returning it to the
+ * caller. The scale factor is gained from the channel configuration
+ * passed via the platform data when the device was registered.
+ */
+static ssize_t s3c_hwmon_ch_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr);
+ struct s3c_hwmon *hwmon = platform_get_drvdata(to_platform_device(dev));
+ struct s3c_hwmon_pdata *pdata = dev->platform_data;
+ struct s3c_hwmon_chcfg *cfg;
+ int ret;
+
+ cfg = pdata->in[sen_attr->index];
+
+ ret = s3c_hwmon_read_ch(dev, hwmon, sen_attr->index);
+ if (ret < 0)
+ return ret;
+
+ ret *= cfg->mult;
+ ret = DIV_ROUND_CLOSEST(ret, cfg->div);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+/**
+ * s3c_hwmon_label_show - show label name of the given channel.
+ * @dev: The device that the attribute belongs to.
+ * @attr: The attribute being read.
+ * @buf: The result buffer.
+ *
+ * Return the label name of a given channel
+ */
+static ssize_t s3c_hwmon_label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sen_attr = to_sensor_dev_attr(attr);
+ struct s3c_hwmon_pdata *pdata = dev->platform_data;
+ struct s3c_hwmon_chcfg *cfg;
+
+ cfg = pdata->in[sen_attr->index];
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", cfg->name);
+}
+
+/**
+ * s3c_hwmon_create_attr - create hwmon attribute for given channel.
+ * @dev: The device to create the attribute on.
+ * @cfg: The channel configuration passed from the platform data.
+ * @channel: The ADC channel number to process.
+ *
+ * Create the scaled attribute for use with hwmon from the specified
+ * platform data in @pdata. The sysfs entry is handled by the routine
+ * s3c_hwmon_ch_show().
+ *
+ * The attribute name is taken from the configuration data if present
+ * otherwise the name is taken by concatenating in_ with the channel
+ * number.
+ */
+static int s3c_hwmon_create_attr(struct device *dev,
+ struct s3c_hwmon_chcfg *cfg,
+ struct s3c_hwmon_attr *attrs,
+ int channel)
+{
+ struct sensor_device_attribute *attr;
+ int ret;
+
+ snprintf(attrs->in_name, sizeof(attrs->in_name), "in%d_input", channel);
+
+ attr = &attrs->in;
+ attr->index = channel;
+ attr->dev_attr.attr.name = attrs->in_name;
+ attr->dev_attr.attr.mode = S_IRUGO;
+ attr->dev_attr.attr.owner = THIS_MODULE;
+ attr->dev_attr.show = s3c_hwmon_ch_show;
+
+ ret = device_create_file(dev, &attr->dev_attr);
+ if (ret < 0) {
+ dev_err(dev, "failed to create input attribute\n");
+ return ret;
+ }
+
+ /* if this has a name, add a label */
+ if (cfg->name) {
+ snprintf(attrs->label_name, sizeof(attrs->label_name),
+ "in%d_label", channel);
+
+ attr = &attrs->label;
+ attr->index = channel;
+ attr->dev_attr.attr.name = attrs->label_name;
+ attr->dev_attr.attr.mode = S_IRUGO;
+ attr->dev_attr.attr.owner = THIS_MODULE;
+ attr->dev_attr.show = s3c_hwmon_label_show;
+
+ ret = device_create_file(dev, &attr->dev_attr);
+ if (ret < 0) {
+ device_remove_file(dev, &attrs->in.dev_attr);
+ dev_err(dev, "failed to create label attribute\n");
+ }
+ }
+
+ return ret;
+}
+
+static void s3c_hwmon_remove_attr(struct device *dev,
+ struct s3c_hwmon_attr *attrs)
+{
+ device_remove_file(dev, &attrs->in.dev_attr);
+ device_remove_file(dev, &attrs->label.dev_attr);
+}
+
+/**
+ * s3c_hwmon_probe - device probe entry.
+ * @dev: The device being probed.
+*/
+static int __devinit s3c_hwmon_probe(struct platform_device *dev)
+{
+ struct s3c_hwmon_pdata *pdata = dev->dev.platform_data;
+ struct s3c_hwmon *hwmon;
+ int ret = 0;
+ int i;
+
+ if (!pdata) {
+ dev_err(&dev->dev, "no platform data supplied\n");
+ return -EINVAL;
+ }
+
+ hwmon = kzalloc(sizeof(struct s3c_hwmon), GFP_KERNEL);
+ if (hwmon == NULL) {
+ dev_err(&dev->dev, "no memory\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(dev, hwmon);
+
+ init_MUTEX(&hwmon->lock);
+
+ /* Register with the core ADC driver. */
+
+ hwmon->client = s3c_adc_register(dev, NULL, NULL, 0);
+ if (IS_ERR(hwmon->client)) {
+ dev_err(&dev->dev, "cannot register adc\n");
+ ret = PTR_ERR(hwmon->client);
+ goto err_mem;
+ }
+
+ /* add attributes for our adc devices. */
+
+ ret = s3c_hwmon_add_raw(&dev->dev);
+ if (ret)
+ goto err_registered;
+
+ /* register with the hwmon core */
+
+ hwmon->hwmon_dev = hwmon_device_register(&dev->dev);
+ if (IS_ERR(hwmon->hwmon_dev)) {
+ dev_err(&dev->dev, "error registering with hwmon\n");
+ ret = PTR_ERR(hwmon->hwmon_dev);
+ goto err_raw_attribute;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pdata->in); i++) {
+ if (!pdata->in[i])
+ continue;
+
+ if (pdata->in[i]->mult >= 0x10000)
+ dev_warn(&dev->dev,
+ "channel %d multiplier too large\n",
+ i);
+
+ ret = s3c_hwmon_create_attr(&dev->dev, pdata->in[i],
+ &hwmon->attrs[i], i);
+ if (ret) {
+ dev_err(&dev->dev,
+ "error creating channel %d\n", i);
+
+ for (i--; i >= 0; i--)
+ s3c_hwmon_remove_attr(&dev->dev,
+ &hwmon->attrs[i]);
+
+ goto err_hwmon_register;
+ }
+ }
+
+ return 0;
+
+ err_hwmon_register:
+ hwmon_device_unregister(hwmon->hwmon_dev);
+
+ err_raw_attribute:
+ s3c_hwmon_remove_raw(&dev->dev);
+
+ err_registered:
+ s3c_adc_release(hwmon->client);
+
+ err_mem:
+ kfree(hwmon);
+ return ret;
+}
+
+static int __devexit s3c_hwmon_remove(struct platform_device *dev)
+{
+ struct s3c_hwmon *hwmon = platform_get_drvdata(dev);
+ int i;
+
+ s3c_hwmon_remove_raw(&dev->dev);
+
+ for (i = 0; i < ARRAY_SIZE(hwmon->attrs); i++)
+ s3c_hwmon_remove_attr(&dev->dev, &hwmon->attrs[i]);
+
+ hwmon_device_unregister(hwmon->hwmon_dev);
+ s3c_adc_release(hwmon->client);
+
+ return 0;
+}
+
+static struct platform_driver s3c_hwmon_driver = {
+ .driver = {
+ .name = "s3c-hwmon",
+ .owner = THIS_MODULE,
+ },
+ .probe = s3c_hwmon_probe,
+ .remove = __devexit_p(s3c_hwmon_remove),
+};
+
+static int __init s3c_hwmon_init(void)
+{
+ return platform_driver_register(&s3c_hwmon_driver);
+}
+
+static void __exit s3c_hwmon_exit(void)
+{
+ platform_driver_unregister(&s3c_hwmon_driver);
+}
+
+module_init(s3c_hwmon_init);
+module_exit(s3c_hwmon_exit);
+
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("S3C ADC HWMon driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:s3c-hwmon");
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 9a1d55b74d7..901b2525993 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -452,6 +452,76 @@ static unsigned int joydev_poll(struct file *file, poll_table *wait)
(joydev->exist ? 0 : (POLLHUP | POLLERR));
}
+static int joydev_handle_JSIOCSAXMAP(struct joydev *joydev,
+ void __user *argp, size_t len)
+{
+ __u8 *abspam;
+ int i;
+ int retval = 0;
+
+ len = min(len, sizeof(joydev->abspam));
+
+ /* Validate the map. */
+ abspam = kmalloc(len, GFP_KERNEL);
+ if (!abspam)
+ return -ENOMEM;
+
+ if (copy_from_user(abspam, argp, len)) {
+ retval = -EFAULT;
+ goto out;
+ }
+
+ for (i = 0; i < joydev->nabs; i++) {
+ if (abspam[i] > ABS_MAX) {
+ retval = -EINVAL;
+ goto out;
+ }
+ }
+
+ memcpy(joydev->abspam, abspam, len);
+
+ out:
+ kfree(abspam);
+ return retval;
+}
+
+static int joydev_handle_JSIOCSBTNMAP(struct joydev *joydev,
+ void __user *argp, size_t len)
+{
+ __u16 *keypam;
+ int i;
+ int retval = 0;
+
+ len = min(len, sizeof(joydev->keypam));
+
+ /* Validate the map. */
+ keypam = kmalloc(len, GFP_KERNEL);
+ if (!keypam)
+ return -ENOMEM;
+
+ if (copy_from_user(keypam, argp, len)) {
+ retval = -EFAULT;
+ goto out;
+ }
+
+ for (i = 0; i < joydev->nkey; i++) {
+ if (keypam[i] > KEY_MAX || keypam[i] < BTN_MISC) {
+ retval = -EINVAL;
+ goto out;
+ }
+ }
+
+ memcpy(joydev->keypam, keypam, len);
+
+ for (i = 0; i < joydev->nkey; i++)
+ joydev->keymap[keypam[i] - BTN_MISC] = i;
+
+ out:
+ kfree(keypam);
+ return retval;
+}
+
+
static int joydev_ioctl_common(struct joydev *joydev,
unsigned int cmd, void __user *argp)
{
@@ -512,46 +582,18 @@ static int joydev_ioctl_common(struct joydev *joydev,
switch (cmd & ~IOCSIZE_MASK) {
case (JSIOCSAXMAP & ~IOCSIZE_MASK):
- len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->abspam));
- /*
- * FIXME: we should not copy into our axis map before
- * validating the data.
- */
- if (copy_from_user(joydev->abspam, argp, len))
- return -EFAULT;
-
- for (i = 0; i < joydev->nabs; i++) {
- if (joydev->abspam[i] > ABS_MAX)
- return -EINVAL;
- joydev->absmap[joydev->abspam[i]] = i;
- }
- return 0;
+ return joydev_handle_JSIOCSAXMAP(joydev, argp, _IOC_SIZE(cmd));
case (JSIOCGAXMAP & ~IOCSIZE_MASK):
len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->abspam));
- return copy_to_user(argp, joydev->abspam, len) ? -EFAULT : 0;
+ return copy_to_user(argp, joydev->abspam, len) ? -EFAULT : len;
case (JSIOCSBTNMAP & ~IOCSIZE_MASK):
- len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->keypam));
- /*
- * FIXME: we should not copy into our keymap before
- * validating the data.
- */
- if (copy_from_user(joydev->keypam, argp, len))
- return -EFAULT;
-
- for (i = 0; i < joydev->nkey; i++) {
- if (joydev->keypam[i] > KEY_MAX ||
- joydev->keypam[i] < BTN_MISC)
- return -EINVAL;
- joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
- }
-
- return 0;
+ return joydev_handle_JSIOCSBTNMAP(joydev, argp, _IOC_SIZE(cmd));
case (JSIOCGBTNMAP & ~IOCSIZE_MASK):
len = min_t(size_t, _IOC_SIZE(cmd), sizeof(joydev->keypam));
- return copy_to_user(argp, joydev->keypam, len) ? -EFAULT : 0;
+ return copy_to_user(argp, joydev->keypam, len) ? -EFAULT : len;
case JSIOCGNAME(0):
name = dev->name;
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index f155ad8cdae..2388cf578a6 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -144,6 +144,7 @@ static const struct xpad_device {
{ 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
{ 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
+ { 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
{ 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_UNKNOWN }
};
@@ -208,6 +209,7 @@ static struct usb_device_id xpad_table [] = {
XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */
+ XPAD_XBOX360_VENDOR(0x1bad), /* Rock Band Drums */
{ }
};
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index a6b989a9dc0..3525c19be42 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -187,7 +187,7 @@ config KEYBOARD_HIL_OLD
submenu.
config KEYBOARD_HIL
- tristate "HP HIL keyboard support"
+ tristate "HP HIL keyboard/pointer support"
depends on GSC || HP300
default y
select HP_SDC
@@ -196,7 +196,8 @@ config KEYBOARD_HIL
help
The "Human Interface Loop" is a older, 8-channel USB-like
controller used in several Hewlett Packard models.
- This driver implements support for HIL-keyboards attached
+ This driver implements support for HIL-keyboards and pointing
+ devices (mice, tablets, touchscreens) attached
to your machine, so normally you should say Y here.
config KEYBOARD_HP6XX
@@ -329,6 +330,17 @@ config KEYBOARD_OMAP
To compile this driver as a module, choose M here: the
module will be called omap-keypad.
+config KEYBOARD_TWL4030
+ tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
+ depends on TWL4030_CORE
+ help
+ Say Y here if your board use the keypad controller on
+ TWL4030 family chips. It's safe to say enable this
+ even on boards that don't use the keypad controller.
+
+ To compile this driver as a module, choose M here: the
+ module will be called twl4030_keypad.
+
config KEYBOARD_TOSA
tristate "Tosa keyboard"
depends on MACH_TOSA
@@ -361,4 +373,14 @@ config KEYBOARD_XTKBD
To compile this driver as a module, choose M here: the
module will be called xtkbd.
+config KEYBOARD_W90P910
+ tristate "W90P910 Matrix Keypad support"
+ depends on ARCH_W90X900
+ help
+ Say Y here to enable the matrix keypad on evaluation board
+ based on W90P910.
+
+ To compile this driver as a module, choose M here: the
+ module will be called w90p910_keypad.
+
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index b5b5eae9724..8a7a22b3026 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -30,4 +30,6 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
+obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
+obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index 6c6a09b1c0f..c9523e48c6a 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -68,7 +68,9 @@ MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and
* are loadable via a userland utility.
*/
-static const unsigned short atkbd_set2_keycode[512] = {
+#define ATKBD_KEYMAP_SIZE 512
+
+static const unsigned short atkbd_set2_keycode[ATKBD_KEYMAP_SIZE] = {
#ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES
@@ -99,7 +101,7 @@ static const unsigned short atkbd_set2_keycode[512] = {
#endif
};
-static const unsigned short atkbd_set3_keycode[512] = {
+static const unsigned short atkbd_set3_keycode[ATKBD_KEYMAP_SIZE] = {
0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60,
131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62,
@@ -200,8 +202,8 @@ struct atkbd {
char phys[32];
unsigned short id;
- unsigned short keycode[512];
- DECLARE_BITMAP(force_release_mask, 512);
+ unsigned short keycode[ATKBD_KEYMAP_SIZE];
+ DECLARE_BITMAP(force_release_mask, ATKBD_KEYMAP_SIZE);
unsigned char set;
unsigned char translated;
unsigned char extra;
@@ -253,6 +255,7 @@ static struct device_attribute atkbd_attr_##_name = \
__ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name);
ATKBD_DEFINE_ATTR(extra);
+ATKBD_DEFINE_ATTR(force_release);
ATKBD_DEFINE_ATTR(scroll);
ATKBD_DEFINE_ATTR(set);
ATKBD_DEFINE_ATTR(softrepeat);
@@ -272,6 +275,7 @@ ATKBD_DEFINE_RO_ATTR(err_count);
static struct attribute *atkbd_attributes[] = {
&atkbd_attr_extra.attr,
+ &atkbd_attr_force_release.attr,
&atkbd_attr_scroll.attr,
&atkbd_attr_set.attr,
&atkbd_attr_softrepeat.attr,
@@ -934,7 +938,7 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd)
int i, j;
memset(atkbd->keycode, 0, sizeof(atkbd->keycode));
- bitmap_zero(atkbd->force_release_mask, 512);
+ bitmap_zero(atkbd->force_release_mask, ATKBD_KEYMAP_SIZE);
if (atkbd->translated) {
for (i = 0; i < 128; i++) {
@@ -1041,7 +1045,7 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd)
input_dev->keycodesize = sizeof(unsigned short);
input_dev->keycodemax = ARRAY_SIZE(atkbd_set2_keycode);
- for (i = 0; i < 512; i++)
+ for (i = 0; i < ATKBD_KEYMAP_SIZE; i++)
if (atkbd->keycode[i] && atkbd->keycode[i] < ATKBD_SPECIAL)
__set_bit(atkbd->keycode[i], input_dev->keybit);
}
@@ -1309,6 +1313,33 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun
return count;
}
+static ssize_t atkbd_show_force_release(struct atkbd *atkbd, char *buf)
+{
+ size_t len = bitmap_scnlistprintf(buf, PAGE_SIZE - 2,
+ atkbd->force_release_mask, ATKBD_KEYMAP_SIZE);
+
+ buf[len++] = '\n';
+ buf[len] = '\0';
+
+ return len;
+}
+
+static ssize_t atkbd_set_force_release(struct atkbd *atkbd,
+ const char *buf, size_t count)
+{
+ /* 64 bytes on stack should be acceptable */
+ DECLARE_BITMAP(new_mask, ATKBD_KEYMAP_SIZE);
+ int err;
+
+ err = bitmap_parselist(buf, new_mask, ATKBD_KEYMAP_SIZE);
+ if (err)
+ return err;
+
+ memcpy(atkbd->force_release_mask, new_mask, sizeof(atkbd->force_release_mask));
+ return count;
+}
+
+
static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf)
{
return sprintf(buf, "%d\n", atkbd->scroll ? 1 : 0);
diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c
index d427f322e20..fe376a27fe5 100644
--- a/drivers/input/keyboard/bf54x-keys.c
+++ b/drivers/input/keyboard/bf54x-keys.c
@@ -184,14 +184,13 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev)
int i, error;
if (!pdata->rows || !pdata->cols || !pdata->keymap) {
- printk(KERN_ERR DRV_NAME
- ": No rows, cols or keymap from pdata\n");
+ dev_err(&pdev->dev, "no rows, cols or keymap from pdata\n");
return -EINVAL;
}
if (!pdata->keymapsize ||
pdata->keymapsize > (pdata->rows * pdata->cols)) {
- printk(KERN_ERR DRV_NAME ": Invalid keymapsize\n");
+ dev_err(&pdev->dev, "invalid keymapsize\n");
return -EINVAL;
}
@@ -211,8 +210,8 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev)
if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT ||
!pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) {
- printk(KERN_WARNING DRV_NAME
- ": Invalid Debounce/Columndrive Time in platform data\n");
+ dev_warn(&pdev->dev,
+ "invalid platform debounce/columndrive time\n");
bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */
} else {
bfin_write_KPAD_MSEL(
@@ -231,16 +230,14 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev)
if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows],
DRV_NAME)) {
- printk(KERN_ERR DRV_NAME
- ": Requesting Peripherals failed\n");
+ dev_err(&pdev->dev, "requesting peripherals failed\n");
error = -EFAULT;
goto out0;
}
if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols],
DRV_NAME)) {
- printk(KERN_ERR DRV_NAME
- ": Requesting Peripherals failed\n");
+ dev_err(&pdev->dev, "requesting peripherals failed\n");
error = -EFAULT;
goto out1;
}
@@ -254,9 +251,8 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev)
error = request_irq(bf54x_kpad->irq, bfin_kpad_isr,
0, DRV_NAME, pdev);
if (error) {
- printk(KERN_ERR DRV_NAME
- ": unable to claim irq %d; error %d\n",
- bf54x_kpad->irq, error);
+ dev_err(&pdev->dev, "unable to claim irq %d\n",
+ bf54x_kpad->irq);
goto out2;
}
@@ -297,8 +293,7 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev)
error = input_register_device(input);
if (error) {
- printk(KERN_ERR DRV_NAME
- ": Unable to register input device (%d)\n", error);
+ dev_err(&pdev->dev, "unable to register input device\n");
goto out4;
}
@@ -316,9 +311,6 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1);
- printk(KERN_ERR DRV_NAME
- ": Blackfin BF54x Keypad registered IRQ %d\n", bf54x_kpad->irq);
-
return 0;
out4:
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index efed0c9e242..a88aff3816a 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -216,8 +216,9 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
-static int gpio_keys_suspend(struct platform_device *pdev, pm_message_t state)
+static int gpio_keys_suspend(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
int i;
@@ -234,8 +235,9 @@ static int gpio_keys_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int gpio_keys_resume(struct platform_device *pdev)
+static int gpio_keys_resume(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
int i;
@@ -251,19 +253,22 @@ static int gpio_keys_resume(struct platform_device *pdev)
return 0;
}
-#else
-#define gpio_keys_suspend NULL
-#define gpio_keys_resume NULL
+
+static const struct dev_pm_ops gpio_keys_pm_ops = {
+ .suspend = gpio_keys_suspend,
+ .resume = gpio_keys_resume,
+};
#endif
static struct platform_driver gpio_keys_device_driver = {
.probe = gpio_keys_probe,
.remove = __devexit_p(gpio_keys_remove),
- .suspend = gpio_keys_suspend,
- .resume = gpio_keys_resume,
.driver = {
.name = "gpio-keys",
.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &gpio_keys_pm_ops,
+#endif
}
};
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
index 6f356705ee3..c83f4b2ec7d 100644
--- a/drivers/input/keyboard/hil_kbd.c
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -37,19 +37,19 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/semaphore.h>
+#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/pci_ids.h>
-#define PREFIX "HIL KEYB: "
-#define HIL_GENERIC_NAME "HIL keyboard"
+#define PREFIX "HIL: "
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
-MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
+MODULE_DESCRIPTION("HIL keyboard/mouse driver");
MODULE_LICENSE("Dual BSD/GPL");
-MODULE_ALIAS("serio:ty03pr25id00ex*");
+MODULE_ALIAS("serio:ty03pr25id00ex*"); /* HIL keyboard */
+MODULE_ALIAS("serio:ty03pr25id0Fex*"); /* HIL mouse */
-#define HIL_KBD_MAX_LENGTH 16
+#define HIL_PACKET_MAX_LENGTH 16
#define HIL_KBD_SET1_UPBIT 0x01
#define HIL_KBD_SET1_SHIFT 1
@@ -67,308 +67,497 @@ static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] __read_mostly =
static const char hil_language[][16] = { HIL_LOCALE_MAP };
-struct hil_kbd {
+struct hil_dev {
struct input_dev *dev;
struct serio *serio;
/* Input buffer and index for packets from HIL bus. */
- hil_packet data[HIL_KBD_MAX_LENGTH];
+ hil_packet data[HIL_PACKET_MAX_LENGTH];
int idx4; /* four counts per packet */
/* Raw device info records from HIL bus, see hil.h for fields. */
- char idd[HIL_KBD_MAX_LENGTH]; /* DID byte and IDD record */
- char rsc[HIL_KBD_MAX_LENGTH]; /* RSC record */
- char exd[HIL_KBD_MAX_LENGTH]; /* EXD record */
- char rnm[HIL_KBD_MAX_LENGTH + 1]; /* RNM record + NULL term. */
+ char idd[HIL_PACKET_MAX_LENGTH]; /* DID byte and IDD record */
+ char rsc[HIL_PACKET_MAX_LENGTH]; /* RSC record */
+ char exd[HIL_PACKET_MAX_LENGTH]; /* EXD record */
+ char rnm[HIL_PACKET_MAX_LENGTH + 1]; /* RNM record + NULL term. */
- /* Something to sleep around with. */
- struct semaphore sem;
+ struct completion cmd_done;
+
+ bool is_pointer;
+ /* Extra device details needed for pointing devices. */
+ unsigned int nbtn, naxes;
+ unsigned int btnmap[7];
};
-/* Process a complete packet after transfer from the HIL */
-static void hil_kbd_process_record(struct hil_kbd *kbd)
+static bool hil_dev_is_command_response(hil_packet p)
{
- struct input_dev *dev = kbd->dev;
- hil_packet *data = kbd->data;
- hil_packet p;
- int idx, i, cnt;
+ if ((p & ~HIL_CMDCT_POL) == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
+ return false;
- idx = kbd->idx4/4;
- p = data[idx - 1];
+ if ((p & ~HIL_CMDCT_RPL) == (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL))
+ return false;
- if ((p & ~HIL_CMDCT_POL) ==
- (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
- goto report;
- if ((p & ~HIL_CMDCT_RPL) ==
- (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL))
- goto report;
+ return true;
+}
+
+static void hil_dev_handle_command_response(struct hil_dev *dev)
+{
+ hil_packet p;
+ char *buf;
+ int i, idx;
+
+ idx = dev->idx4 / 4;
+ p = dev->data[idx - 1];
- /* Not a poll response. See if we are loading config records. */
switch (p & HIL_PKT_DATA_MASK) {
case HIL_CMD_IDD:
- for (i = 0; i < idx; i++)
- kbd->idd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
- for (; i < HIL_KBD_MAX_LENGTH; i++)
- kbd->idd[i] = 0;
+ buf = dev->idd;
break;
case HIL_CMD_RSC:
- for (i = 0; i < idx; i++)
- kbd->rsc[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
- for (; i < HIL_KBD_MAX_LENGTH; i++)
- kbd->rsc[i] = 0;
+ buf = dev->rsc;
break;
case HIL_CMD_EXD:
- for (i = 0; i < idx; i++)
- kbd->exd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
- for (; i < HIL_KBD_MAX_LENGTH; i++)
- kbd->exd[i] = 0;
+ buf = dev->exd;
break;
case HIL_CMD_RNM:
- for (i = 0; i < idx; i++)
- kbd->rnm[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
- for (; i < HIL_KBD_MAX_LENGTH + 1; i++)
- kbd->rnm[i] = '\0';
+ dev->rnm[HIL_PACKET_MAX_LENGTH] = 0;
+ buf = dev->rnm;
break;
default:
/* These occur when device isn't present */
- if (p == (HIL_ERR_INT | HIL_PKT_CMD))
- break;
- /* Anything else we'd like to know about. */
- printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
- break;
+ if (p != (HIL_ERR_INT | HIL_PKT_CMD)) {
+ /* Anything else we'd like to know about. */
+ printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
+ }
+ goto out;
}
- goto out;
- report:
- cnt = 1;
+ for (i = 0; i < idx; i++)
+ buf[i] = dev->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_PACKET_MAX_LENGTH; i++)
+ buf[i] = 0;
+ out:
+ complete(&dev->cmd_done);
+}
+
+static void hil_dev_handle_kbd_events(struct hil_dev *kbd)
+{
+ struct input_dev *dev = kbd->dev;
+ int idx = kbd->idx4 / 4;
+ int i;
+
switch (kbd->data[0] & HIL_POL_CHARTYPE_MASK) {
case HIL_POL_CHARTYPE_NONE:
- break;
+ return;
case HIL_POL_CHARTYPE_ASCII:
- while (cnt < idx - 1)
- input_report_key(dev, kbd->data[cnt++] & 0x7f, 1);
+ for (i = 1; i < idx - 1; i++)
+ input_report_key(dev, kbd->data[i] & 0x7f, 1);
break;
case HIL_POL_CHARTYPE_RSVD1:
case HIL_POL_CHARTYPE_RSVD2:
case HIL_POL_CHARTYPE_BINARY:
- while (cnt < idx - 1)
- input_report_key(dev, kbd->data[cnt++], 1);
+ for (i = 1; i < idx - 1; i++)
+ input_report_key(dev, kbd->data[i], 1);
break;
case HIL_POL_CHARTYPE_SET1:
- while (cnt < idx - 1) {
- unsigned int key;
- int up;
- key = kbd->data[cnt++];
- up = key & HIL_KBD_SET1_UPBIT;
+ for (i = 1; i < idx - 1; i++) {
+ unsigned int key = kbd->data[i];
+ int up = key & HIL_KBD_SET1_UPBIT;
+
key &= (~HIL_KBD_SET1_UPBIT & 0xff);
key = hil_kbd_set1[key >> HIL_KBD_SET1_SHIFT];
- if (key != KEY_RESERVED)
- input_report_key(dev, key, !up);
+ input_report_key(dev, key, !up);
}
break;
case HIL_POL_CHARTYPE_SET2:
- while (cnt < idx - 1) {
- unsigned int key;
- int up;
- key = kbd->data[cnt++];
- up = key & HIL_KBD_SET2_UPBIT;
+ for (i = 1; i < idx - 1; i++) {
+ unsigned int key = kbd->data[i];
+ int up = key & HIL_KBD_SET2_UPBIT;
+
key &= (~HIL_KBD_SET1_UPBIT & 0xff);
key = key >> HIL_KBD_SET2_SHIFT;
- if (key != KEY_RESERVED)
- input_report_key(dev, key, !up);
+ input_report_key(dev, key, !up);
}
break;
case HIL_POL_CHARTYPE_SET3:
- while (cnt < idx - 1) {
- unsigned int key;
- int up;
- key = kbd->data[cnt++];
- up = key & HIL_KBD_SET3_UPBIT;
+ for (i = 1; i < idx - 1; i++) {
+ unsigned int key = kbd->data[i];
+ int up = key & HIL_KBD_SET3_UPBIT;
+
key &= (~HIL_KBD_SET1_UPBIT & 0xff);
key = hil_kbd_set3[key >> HIL_KBD_SET3_SHIFT];
- if (key != KEY_RESERVED)
- input_report_key(dev, key, !up);
+ input_report_key(dev, key, !up);
}
break;
}
- out:
- kbd->idx4 = 0;
- up(&kbd->sem);
+
+ input_sync(dev);
}
-static void hil_kbd_process_err(struct hil_kbd *kbd)
+static void hil_dev_handle_ptr_events(struct hil_dev *ptr)
+{
+ struct input_dev *dev = ptr->dev;
+ int idx = ptr->idx4 / 4;
+ hil_packet p = ptr->data[idx - 1];
+ int i, cnt, laxis;
+ bool absdev, ax16;
+
+ if ((p & HIL_CMDCT_POL) != idx - 1) {
+ printk(KERN_WARNING PREFIX
+ "Malformed poll packet %x (idx = %i)\n", p, idx);
+ return;
+ }
+
+ i = (p & HIL_POL_AXIS_ALT) ? 3 : 0;
+ laxis = (p & HIL_POL_NUM_AXES_MASK) + i;
+
+ ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */
+ absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS;
+
+ for (cnt = 1; i < laxis; i++) {
+ unsigned int lo, hi, val;
+
+ lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK;
+ hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0;
+
+ if (absdev) {
+ val = lo + (hi << 8);
+#ifdef TABLET_AUTOADJUST
+ if (val < dev->absmin[ABS_X + i])
+ dev->absmin[ABS_X + i] = val;
+ if (val > dev->absmax[ABS_X + i])
+ dev->absmax[ABS_X + i] = val;
+#endif
+ if (i%3) val = dev->absmax[ABS_X + i] - val;
+ input_report_abs(dev, ABS_X + i, val);
+ } else {
+ val = (int) (((int8_t)lo) | ((int8_t)hi << 8));
+ if (i % 3)
+ val *= -1;
+ input_report_rel(dev, REL_X + i, val);
+ }
+ }
+
+ while (cnt < idx - 1) {
+ unsigned int btn = ptr->data[cnt++];
+ int up = btn & 1;
+
+ btn &= 0xfe;
+ if (btn == 0x8e)
+ continue; /* TODO: proximity == touch? */
+ if (btn > 0x8c || btn < 0x80)
+ continue;
+ btn = (btn - 0x80) >> 1;
+ btn = ptr->btnmap[btn];
+ input_report_key(dev, btn, !up);
+ }
+
+ input_sync(dev);
+}
+
+static void hil_dev_process_err(struct hil_dev *dev)
{
printk(KERN_WARNING PREFIX "errored HIL packet\n");
- kbd->idx4 = 0;
- up(&kbd->sem);
+ dev->idx4 = 0;
+ complete(&dev->cmd_done); /* just in case somebody is waiting */
}
-static irqreturn_t hil_kbd_interrupt(struct serio *serio,
+static irqreturn_t hil_dev_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
- struct hil_kbd *kbd;
+ struct hil_dev *dev;
hil_packet packet;
int idx;
- kbd = serio_get_drvdata(serio);
- BUG_ON(kbd == NULL);
+ dev = serio_get_drvdata(serio);
+ BUG_ON(dev == NULL);
- if (kbd->idx4 >= (HIL_KBD_MAX_LENGTH * sizeof(hil_packet))) {
- hil_kbd_process_err(kbd);
- return IRQ_HANDLED;
+ if (dev->idx4 >= HIL_PACKET_MAX_LENGTH * sizeof(hil_packet)) {
+ hil_dev_process_err(dev);
+ goto out;
}
- idx = kbd->idx4/4;
- if (!(kbd->idx4 % 4))
- kbd->data[idx] = 0;
- packet = kbd->data[idx];
- packet |= ((hil_packet)data) << ((3 - (kbd->idx4 % 4)) * 8);
- kbd->data[idx] = packet;
+
+ idx = dev->idx4 / 4;
+ if (!(dev->idx4 % 4))
+ dev->data[idx] = 0;
+ packet = dev->data[idx];
+ packet |= ((hil_packet)data) << ((3 - (dev->idx4 % 4)) * 8);
+ dev->data[idx] = packet;
/* Records of N 4-byte hil_packets must terminate with a command. */
- if ((++(kbd->idx4)) % 4)
- return IRQ_HANDLED;
- if ((packet & 0xffff0000) != HIL_ERR_INT) {
- hil_kbd_process_err(kbd);
- return IRQ_HANDLED;
+ if ((++dev->idx4 % 4) == 0) {
+ if ((packet & 0xffff0000) != HIL_ERR_INT) {
+ hil_dev_process_err(dev);
+ } else if (packet & HIL_PKT_CMD) {
+ if (hil_dev_is_command_response(packet))
+ hil_dev_handle_command_response(dev);
+ else if (dev->is_pointer)
+ hil_dev_handle_ptr_events(dev);
+ else
+ hil_dev_handle_kbd_events(dev);
+ dev->idx4 = 0;
+ }
}
- if (packet & HIL_PKT_CMD)
- hil_kbd_process_record(kbd);
+ out:
return IRQ_HANDLED;
}
-static void hil_kbd_disconnect(struct serio *serio)
+static void hil_dev_disconnect(struct serio *serio)
{
- struct hil_kbd *kbd;
+ struct hil_dev *dev = serio_get_drvdata(serio);
- kbd = serio_get_drvdata(serio);
- BUG_ON(kbd == NULL);
+ BUG_ON(dev == NULL);
serio_close(serio);
- input_unregister_device(kbd->dev);
- kfree(kbd);
+ input_unregister_device(dev->dev);
+ serio_set_drvdata(serio, NULL);
+ kfree(dev);
+}
+
+static void hil_dev_keyboard_setup(struct hil_dev *kbd)
+{
+ struct input_dev *input_dev = kbd->dev;
+ uint8_t did = kbd->idd[0];
+ int i;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ input_dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
+ BIT_MASK(LED_SCROLLL);
+
+ for (i = 0; i < 128; i++) {
+ __set_bit(hil_kbd_set1[i], input_dev->keybit);
+ __set_bit(hil_kbd_set3[i], input_dev->keybit);
+ }
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ input_dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
+ input_dev->keycodesize = sizeof(hil_kbd_set1[0]);
+ input_dev->keycode = hil_kbd_set1;
+
+ input_dev->name = strlen(kbd->rnm) ? kbd->rnm : "HIL keyboard";
+ input_dev->phys = "hpkbd/input0";
+
+ printk(KERN_INFO PREFIX "HIL keyboard found (did = 0x%02x, lang = %s)\n",
+ did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]);
}
-static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
+static void hil_dev_pointer_setup(struct hil_dev *ptr)
{
- struct hil_kbd *kbd;
- uint8_t did, *idd;
- int i;
+ struct input_dev *input_dev = ptr->dev;
+ uint8_t did = ptr->idd[0];
+ uint8_t *idd = ptr->idd + 1;
+ unsigned int naxsets = HIL_IDD_NUM_AXSETS(*idd);
+ unsigned int i, btntype;
+ const char *txt;
+
+ ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd);
+
+ switch (did & HIL_IDD_DID_TYPE_MASK) {
+ case HIL_IDD_DID_TYPE_REL:
+ input_dev->evbit[0] = BIT_MASK(EV_REL);
- kbd = kzalloc(sizeof(*kbd), GFP_KERNEL);
- if (!kbd)
- return -ENOMEM;
+ for (i = 0; i < ptr->naxes; i++)
+ __set_bit(REL_X + i, input_dev->relbit);
- kbd->dev = input_allocate_device();
- if (!kbd->dev)
+ for (i = 3; naxsets > 1 && i < ptr->naxes + 3; i++)
+ __set_bit(REL_X + i, input_dev->relbit);
+
+ txt = "relative";
+ break;
+
+ case HIL_IDD_DID_TYPE_ABS:
+ input_dev->evbit[0] = BIT_MASK(EV_ABS);
+
+ for (i = 0; i < ptr->naxes; i++)
+ input_set_abs_params(input_dev, ABS_X + i,
+ 0, HIL_IDD_AXIS_MAX(idd, i), 0, 0);
+
+ for (i = 3; naxsets > 1 && i < ptr->naxes + 3; i++)
+ input_set_abs_params(input_dev, ABS_X + i,
+ 0, HIL_IDD_AXIS_MAX(idd, i - 3), 0, 0);
+
+#ifdef TABLET_AUTOADJUST
+ for (i = 0; i < ABS_MAX; i++) {
+ int diff = input_dev->absmax[ABS_X + i] / 10;
+ input_dev->absmin[ABS_X + i] += diff;
+ input_dev->absmax[ABS_X + i] -= diff;
+ }
+#endif
+
+ txt = "absolute";
+ break;
+
+ default:
+ BUG();
+ }
+
+ ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd);
+ if (ptr->nbtn)
+ input_dev->evbit[0] |= BIT_MASK(EV_KEY);
+
+ btntype = BTN_MISC;
+ if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET)
+#ifdef TABLET_SIMULATES_MOUSE
+ btntype = BTN_TOUCH;
+#else
+ btntype = BTN_DIGI;
+#endif
+ if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN)
+ btntype = BTN_TOUCH;
+
+ if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE)
+ btntype = BTN_MOUSE;
+
+ for (i = 0; i < ptr->nbtn; i++) {
+ __set_bit(btntype | i, input_dev->keybit);
+ ptr->btnmap[i] = btntype | i;
+ }
+
+ if (btntype == BTN_MOUSE) {
+ /* Swap buttons 2 and 3 */
+ ptr->btnmap[1] = BTN_MIDDLE;
+ ptr->btnmap[2] = BTN_RIGHT;
+ }
+
+ input_dev->name = strlen(ptr->rnm) ? ptr->rnm : "HIL pointer device";
+
+ printk(KERN_INFO PREFIX
+ "HIL pointer device found (did: 0x%02x, axis: %s)\n",
+ did, txt);
+ printk(KERN_INFO PREFIX
+ "HIL pointer has %i buttons and %i sets of %i axes\n",
+ ptr->nbtn, naxsets, ptr->naxes);
+}
+
+static int hil_dev_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct hil_dev *dev;
+ struct input_dev *input_dev;
+ uint8_t did, *idd;
+ int error;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!dev || !input_dev) {
+ error = -ENOMEM;
goto bail0;
+ }
- if (serio_open(serio, drv))
- goto bail1;
+ dev->serio = serio;
+ dev->dev = input_dev;
- serio_set_drvdata(serio, kbd);
- kbd->serio = serio;
+ error = serio_open(serio, drv);
+ if (error)
+ goto bail0;
- init_MUTEX_LOCKED(&kbd->sem);
+ serio_set_drvdata(serio, dev);
/* Get device info. MLC driver supplies devid/status/etc. */
- serio->write(serio, 0);
- serio->write(serio, 0);
- serio->write(serio, HIL_PKT_CMD >> 8);
- serio->write(serio, HIL_CMD_IDD);
- down(&kbd->sem);
-
- serio->write(serio, 0);
- serio->write(serio, 0);
- serio->write(serio, HIL_PKT_CMD >> 8);
- serio->write(serio, HIL_CMD_RSC);
- down(&kbd->sem);
-
- serio->write(serio, 0);
- serio->write(serio, 0);
- serio->write(serio, HIL_PKT_CMD >> 8);
- serio->write(serio, HIL_CMD_RNM);
- down(&kbd->sem);
-
- serio->write(serio, 0);
- serio->write(serio, 0);
- serio->write(serio, HIL_PKT_CMD >> 8);
- serio->write(serio, HIL_CMD_EXD);
- down(&kbd->sem);
-
- up(&kbd->sem);
-
- did = kbd->idd[0];
- idd = kbd->idd + 1;
+ init_completion(&dev->cmd_done);
+ serio_write(serio, 0);
+ serio_write(serio, 0);
+ serio_write(serio, HIL_PKT_CMD >> 8);
+ serio_write(serio, HIL_CMD_IDD);
+ error = wait_for_completion_killable(&dev->cmd_done);
+ if (error)
+ goto bail1;
+
+ init_completion(&dev->cmd_done);
+ serio_write(serio, 0);
+ serio_write(serio, 0);
+ serio_write(serio, HIL_PKT_CMD >> 8);
+ serio_write(serio, HIL_CMD_RSC);
+ error = wait_for_completion_killable(&dev->cmd_done);
+ if (error)
+ goto bail1;
+
+ init_completion(&dev->cmd_done);
+ serio_write(serio, 0);
+ serio_write(serio, 0);
+ serio_write(serio, HIL_PKT_CMD >> 8);
+ serio_write(serio, HIL_CMD_RNM);
+ error = wait_for_completion_killable(&dev->cmd_done);
+ if (error)
+ goto bail1;
+
+ init_completion(&dev->cmd_done);
+ serio_write(serio, 0);
+ serio_write(serio, 0);
+ serio_write(serio, HIL_PKT_CMD >> 8);
+ serio_write(serio, HIL_CMD_EXD);
+ error = wait_for_completion_killable(&dev->cmd_done);
+ if (error)
+ goto bail1;
+
+ did = dev->idd[0];
+ idd = dev->idd + 1;
+
switch (did & HIL_IDD_DID_TYPE_MASK) {
case HIL_IDD_DID_TYPE_KB_INTEGRAL:
case HIL_IDD_DID_TYPE_KB_ITF:
case HIL_IDD_DID_TYPE_KB_RSVD:
case HIL_IDD_DID_TYPE_CHAR:
- printk(KERN_INFO PREFIX "HIL keyboard found (did = 0x%02x, lang = %s)\n",
- did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]);
- break;
- default:
- goto bail2;
- }
+ if (HIL_IDD_NUM_BUTTONS(idd) ||
+ HIL_IDD_NUM_AXES_PER_SET(*idd)) {
+ printk(KERN_INFO PREFIX
+ "combo devices are not supported.\n");
+ goto bail1;
+ }
- if (HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) {
- printk(KERN_INFO PREFIX "keyboards only, no combo devices supported.\n");
- goto bail2;
- }
+ dev->is_pointer = false;
+ hil_dev_keyboard_setup(dev);
+ break;
- kbd->dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
- kbd->dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
- BIT_MASK(LED_SCROLLL);
- kbd->dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
- kbd->dev->keycodesize = sizeof(hil_kbd_set1[0]);
- kbd->dev->keycode = hil_kbd_set1;
- kbd->dev->name = strlen(kbd->rnm) ? kbd->rnm : HIL_GENERIC_NAME;
- kbd->dev->phys = "hpkbd/input0"; /* XXX */
-
- kbd->dev->id.bustype = BUS_HIL;
- kbd->dev->id.vendor = PCI_VENDOR_ID_HP;
- kbd->dev->id.product = 0x0001; /* TODO: get from kbd->rsc */
- kbd->dev->id.version = 0x0100; /* TODO: get from kbd->rsc */
- kbd->dev->dev.parent = &serio->dev;
+ case HIL_IDD_DID_TYPE_REL:
+ case HIL_IDD_DID_TYPE_ABS:
+ dev->is_pointer = true;
+ hil_dev_pointer_setup(dev);
+ break;
- for (i = 0; i < 128; i++) {
- set_bit(hil_kbd_set1[i], kbd->dev->keybit);
- set_bit(hil_kbd_set3[i], kbd->dev->keybit);
+ default:
+ goto bail1;
}
- clear_bit(0, kbd->dev->keybit);
- input_register_device(kbd->dev);
- printk(KERN_INFO "input: %s, ID: %d\n",
- kbd->dev->name, did);
+ input_dev->id.bustype = BUS_HIL;
+ input_dev->id.vendor = PCI_VENDOR_ID_HP;
+ input_dev->id.product = 0x0001; /* TODO: get from kbd->rsc */
+ input_dev->id.version = 0x0100; /* TODO: get from kbd->rsc */
+ input_dev->dev.parent = &serio->dev;
+
+ if (!dev->is_pointer) {
+ serio_write(serio, 0);
+ serio_write(serio, 0);
+ serio_write(serio, HIL_PKT_CMD >> 8);
+ /* Enable Keyswitch Autorepeat 1 */
+ serio_write(serio, HIL_CMD_EK1);
+ /* No need to wait for completion */
+ }
- serio->write(serio, 0);
- serio->write(serio, 0);
- serio->write(serio, HIL_PKT_CMD >> 8);
- serio->write(serio, HIL_CMD_EK1); /* Enable Keyswitch Autorepeat 1 */
- down(&kbd->sem);
- up(&kbd->sem);
+ error = input_register_device(input_dev);
+ if (error)
+ goto bail1;
return 0;
- bail2:
+
+ bail1:
serio_close(serio);
serio_set_drvdata(serio, NULL);
- bail1:
- input_free_device(kbd->dev);
bail0:
- kfree(kbd);
- return -EIO;
+ input_free_device(input_dev);
+ kfree(dev);
+ return error;
}
-static struct serio_device_id hil_kbd_ids[] = {
+static struct serio_device_id hil_dev_ids[] = {
{
.type = SERIO_HIL_MLC,
.proto = SERIO_HIL,
@@ -378,26 +567,26 @@ static struct serio_device_id hil_kbd_ids[] = {
{ 0 }
};
-static struct serio_driver hil_kbd_serio_drv = {
+static struct serio_driver hil_serio_drv = {
.driver = {
- .name = "hil_kbd",
+ .name = "hil_dev",
},
- .description = "HP HIL keyboard driver",
- .id_table = hil_kbd_ids,
- .connect = hil_kbd_connect,
- .disconnect = hil_kbd_disconnect,
- .interrupt = hil_kbd_interrupt
+ .description = "HP HIL keyboard/mouse/tablet driver",
+ .id_table = hil_dev_ids,
+ .connect = hil_dev_connect,
+ .disconnect = hil_dev_disconnect,
+ .interrupt = hil_dev_interrupt
};
-static int __init hil_kbd_init(void)
+static int __init hil_dev_init(void)
{
- return serio_register_driver(&hil_kbd_serio_drv);
+ return serio_register_driver(&hil_serio_drv);
}
-static void __exit hil_kbd_exit(void)
+static void __exit hil_dev_exit(void)
{
- serio_unregister_driver(&hil_kbd_serio_drv);
+ serio_unregister_driver(&hil_serio_drv);
}
-module_init(hil_kbd_init);
-module_exit(hil_kbd_exit);
+module_init(hil_dev_init);
+module_exit(hil_dev_exit);
diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c
index 4730ef35c73..f9847e0fb55 100644
--- a/drivers/input/keyboard/lkkbd.c
+++ b/drivers/input/keyboard/lkkbd.c
@@ -525,12 +525,12 @@ lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code,
CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
if (leds_on != 0) {
- lk->serio->write (lk->serio, LK_CMD_LED_ON);
- lk->serio->write (lk->serio, leds_on);
+ serio_write (lk->serio, LK_CMD_LED_ON);
+ serio_write (lk->serio, leds_on);
}
if (leds_off != 0) {
- lk->serio->write (lk->serio, LK_CMD_LED_OFF);
- lk->serio->write (lk->serio, leds_off);
+ serio_write (lk->serio, LK_CMD_LED_OFF);
+ serio_write (lk->serio, leds_off);
}
return 0;
@@ -539,20 +539,20 @@ lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code,
case SND_CLICK:
if (value == 0) {
DBG ("%s: Deactivating key clicks\n", __func__);
- lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
- lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
+ serio_write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
+ serio_write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
} else {
DBG ("%s: Activating key clicks\n", __func__);
- lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
- lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume));
- lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
- lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
+ serio_write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
+ serio_write (lk->serio, volume_to_hw (lk->keyclick_volume));
+ serio_write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
+ serio_write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
}
return 0;
case SND_BELL:
if (value != 0)
- lk->serio->write (lk->serio, LK_CMD_SOUND_BELL);
+ serio_write (lk->serio, LK_CMD_SOUND_BELL);
return 0;
}
@@ -579,10 +579,10 @@ lkkbd_reinit (struct work_struct *work)
unsigned char leds_off = 0;
/* Ask for ID */
- lk->serio->write (lk->serio, LK_CMD_REQUEST_ID);
+ serio_write (lk->serio, LK_CMD_REQUEST_ID);
/* Reset parameters */
- lk->serio->write (lk->serio, LK_CMD_SET_DEFAULTS);
+ serio_write (lk->serio, LK_CMD_SET_DEFAULTS);
/* Set LEDs */
CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
@@ -590,12 +590,12 @@ lkkbd_reinit (struct work_struct *work)
CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
if (leds_on != 0) {
- lk->serio->write (lk->serio, LK_CMD_LED_ON);
- lk->serio->write (lk->serio, leds_on);
+ serio_write (lk->serio, LK_CMD_LED_ON);
+ serio_write (lk->serio, leds_on);
}
if (leds_off != 0) {
- lk->serio->write (lk->serio, LK_CMD_LED_OFF);
- lk->serio->write (lk->serio, leds_off);
+ serio_write (lk->serio, LK_CMD_LED_OFF);
+ serio_write (lk->serio, leds_off);
}
/*
@@ -603,31 +603,31 @@ lkkbd_reinit (struct work_struct *work)
* only work with a LK401 keyboard and grants access to
* LAlt, RAlt, RCompose and RShift.
*/
- lk->serio->write (lk->serio, LK_CMD_ENABLE_LK401);
+ serio_write (lk->serio, LK_CMD_ENABLE_LK401);
/* Set all keys to UPDOWN mode */
for (division = 1; division <= 14; division++)
- lk->serio->write (lk->serio, LK_CMD_SET_MODE (LK_MODE_UPDOWN,
+ serio_write (lk->serio, LK_CMD_SET_MODE (LK_MODE_UPDOWN,
division));
/* Enable bell and set volume */
- lk->serio->write (lk->serio, LK_CMD_ENABLE_BELL);
- lk->serio->write (lk->serio, volume_to_hw (lk->bell_volume));
+ serio_write (lk->serio, LK_CMD_ENABLE_BELL);
+ serio_write (lk->serio, volume_to_hw (lk->bell_volume));
/* Enable/disable keyclick (and possibly set volume) */
if (test_bit (SND_CLICK, lk->dev->snd)) {
- lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
- lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume));
- lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
- lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
+ serio_write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
+ serio_write (lk->serio, volume_to_hw (lk->keyclick_volume));
+ serio_write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
+ serio_write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
} else {
- lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
- lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
+ serio_write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
+ serio_write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
}
/* Sound the bell if needed */
if (test_bit (SND_BELL, lk->dev->snd))
- lk->serio->write (lk->serio, LK_CMD_SOUND_BELL);
+ serio_write (lk->serio, LK_CMD_SOUND_BELL);
}
/*
@@ -684,8 +684,10 @@ lkkbd_connect (struct serio *serio, struct serio_driver *drv)
input_dev->keycode = lk->keycode;
input_dev->keycodesize = sizeof (lk_keycode_t);
input_dev->keycodemax = LK_NUM_KEYCODES;
+
for (i = 0; i < LK_NUM_KEYCODES; i++)
- set_bit (lk->keycode[i], input_dev->keybit);
+ __set_bit (lk->keycode[i], input_dev->keybit);
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
serio_set_drvdata (serio, lk);
@@ -697,7 +699,7 @@ lkkbd_connect (struct serio *serio, struct serio_driver *drv)
if (err)
goto fail3;
- lk->serio->write (lk->serio, LK_CMD_POWERCYCLE_RESET);
+ serio_write (lk->serio, LK_CMD_POWERCYCLE_RESET);
return 0;
diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index 541b981ff07..91cfe517026 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -319,7 +319,6 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
struct input_dev *input_dev;
unsigned short *keycodes;
unsigned int row_shift;
- int i;
int err;
pdata = pdev->dev.platform_data;
@@ -363,18 +362,10 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
input_dev->keycode = keycodes;
input_dev->keycodesize = sizeof(*keycodes);
- input_dev->keycodemax = pdata->num_row_gpios << keypad->row_shift;
-
- for (i = 0; i < keymap_data->keymap_size; i++) {
- unsigned int key = keymap_data->keymap[i];
- unsigned int row = KEY_ROW(key);
- unsigned int col = KEY_COL(key);
- unsigned short code = KEY_VAL(key);
+ input_dev->keycodemax = pdata->num_row_gpios << row_shift;
- keycodes[MATRIX_SCAN_CODE(row, col, row_shift)] = code;
- __set_bit(code, input_dev->keybit);
- }
- __clear_bit(KEY_RESERVED, input_dev->keybit);
+ matrix_keypad_build_keymap(keymap_data, row_shift,
+ input_dev->keycode, input_dev->keybit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, keypad);
diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c
index 0d2fc64a5e1..76f9668221a 100644
--- a/drivers/input/keyboard/pxa27x_keypad.c
+++ b/drivers/input/keyboard/pxa27x_keypad.c
@@ -25,13 +25,13 @@
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/input/matrix_keypad.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <mach/hardware.h>
#include <mach/pxa27x_keypad.h>
-
/*
* Keypad Controller registers
*/
@@ -95,7 +95,8 @@
#define keypad_readl(off) __raw_readl(keypad->mmio_base + (off))
#define keypad_writel(off, v) __raw_writel((v), keypad->mmio_base + (off))
-#define MAX_MATRIX_KEY_NUM (8 * 8)
+#define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS)
+#define MAX_KEYPAD_KEYS (MAX_MATRIX_KEY_NUM + MAX_DIRECT_KEY_NUM)
struct pxa27x_keypad {
struct pxa27x_keypad_platform_data *pdata;
@@ -106,73 +107,82 @@ struct pxa27x_keypad {
int irq;
- /* matrix key code map */
- unsigned int matrix_keycodes[MAX_MATRIX_KEY_NUM];
+ unsigned short keycodes[MAX_KEYPAD_KEYS];
+ int rotary_rel_code[2];
/* state row bits of each column scan */
uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS];
uint32_t direct_key_state;
unsigned int direct_key_mask;
-
- int rotary_rel_code[2];
- int rotary_up_key[2];
- int rotary_down_key[2];
};
static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad)
{
struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
struct input_dev *input_dev = keypad->input_dev;
- unsigned int *key;
+ unsigned short keycode;
int i;
- key = &pdata->matrix_key_map[0];
- for (i = 0; i < pdata->matrix_key_map_size; i++, key++) {
- int row = ((*key) >> 28) & 0xf;
- int col = ((*key) >> 24) & 0xf;
- int code = (*key) & 0xffffff;
+ for (i = 0; i < pdata->matrix_key_map_size; i++) {
+ unsigned int key = pdata->matrix_key_map[i];
+ unsigned int row = KEY_ROW(key);
+ unsigned int col = KEY_COL(key);
+ unsigned int scancode = MATRIX_SCAN_CODE(row, col,
+ MATRIX_ROW_SHIFT);
- keypad->matrix_keycodes[(row << 3) + col] = code;
- set_bit(code, input_dev->keybit);
+ keycode = KEY_VAL(key);
+ keypad->keycodes[scancode] = keycode;
+ __set_bit(keycode, input_dev->keybit);
}
- for (i = 0; i < pdata->direct_key_num; i++)
- set_bit(pdata->direct_key_map[i], input_dev->keybit);
-
- keypad->rotary_up_key[0] = pdata->rotary0_up_key;
- keypad->rotary_up_key[1] = pdata->rotary1_up_key;
- keypad->rotary_down_key[0] = pdata->rotary0_down_key;
- keypad->rotary_down_key[1] = pdata->rotary1_down_key;
- keypad->rotary_rel_code[0] = pdata->rotary0_rel_code;
- keypad->rotary_rel_code[1] = pdata->rotary1_rel_code;
+ for (i = 0; i < pdata->direct_key_num; i++) {
+ keycode = pdata->direct_key_map[i];
+ keypad->keycodes[MAX_MATRIX_KEY_NUM + i] = keycode;
+ __set_bit(keycode, input_dev->keybit);
+ }
if (pdata->enable_rotary0) {
if (pdata->rotary0_up_key && pdata->rotary0_down_key) {
- set_bit(pdata->rotary0_up_key, input_dev->keybit);
- set_bit(pdata->rotary0_down_key, input_dev->keybit);
- } else
- set_bit(pdata->rotary0_rel_code, input_dev->relbit);
+ keycode = pdata->rotary0_up_key;
+ keypad->keycodes[MAX_MATRIX_KEY_NUM + 0] = keycode;
+ __set_bit(keycode, input_dev->keybit);
+
+ keycode = pdata->rotary0_down_key;
+ keypad->keycodes[MAX_MATRIX_KEY_NUM + 1] = keycode;
+ __set_bit(keycode, input_dev->keybit);
+
+ keypad->rotary_rel_code[0] = -1;
+ } else {
+ keypad->rotary_rel_code[0] = pdata->rotary0_rel_code;
+ __set_bit(pdata->rotary0_rel_code, input_dev->relbit);
+ }
}
if (pdata->enable_rotary1) {
if (pdata->rotary1_up_key && pdata->rotary1_down_key) {
- set_bit(pdata->rotary1_up_key, input_dev->keybit);
- set_bit(pdata->rotary1_down_key, input_dev->keybit);
- } else
- set_bit(pdata->rotary1_rel_code, input_dev->relbit);
+ keycode = pdata->rotary1_up_key;
+ keypad->keycodes[MAX_MATRIX_KEY_NUM + 2] = keycode;
+ __set_bit(keycode, input_dev->keybit);
+
+ keycode = pdata->rotary1_down_key;
+ keypad->keycodes[MAX_MATRIX_KEY_NUM + 3] = keycode;
+ __set_bit(keycode, input_dev->keybit);
+
+ keypad->rotary_rel_code[1] = -1;
+ } else {
+ keypad->rotary_rel_code[1] = pdata->rotary1_rel_code;
+ __set_bit(pdata->rotary1_rel_code, input_dev->relbit);
+ }
}
-}
-static inline unsigned int lookup_matrix_keycode(
- struct pxa27x_keypad *keypad, int row, int col)
-{
- return keypad->matrix_keycodes[(row << 3) + col];
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
}
static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad)
{
struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+ struct input_dev *input_dev = keypad->input_dev;
int row, col, num_keys_pressed = 0;
uint32_t new_state[MAX_MATRIX_KEY_COLS];
uint32_t kpas = keypad_readl(KPAS);
@@ -215,6 +225,7 @@ static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad)
scan:
for (col = 0; col < pdata->matrix_key_cols; col++) {
uint32_t bits_changed;
+ int code;
bits_changed = keypad->matrix_key_state[col] ^ new_state[col];
if (bits_changed == 0)
@@ -224,12 +235,13 @@ scan:
if ((bits_changed & (1 << row)) == 0)
continue;
- input_report_key(keypad->input_dev,
- lookup_matrix_keycode(keypad, row, col),
- new_state[col] & (1 << row));
+ code = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT);
+ input_event(input_dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(input_dev, keypad->keycodes[code],
+ new_state[col] & (1 << row));
}
}
- input_sync(keypad->input_dev);
+ input_sync(input_dev);
memcpy(keypad->matrix_key_state, new_state, sizeof(new_state));
}
@@ -252,13 +264,15 @@ static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta)
if (delta == 0)
return;
- if (keypad->rotary_up_key[r] && keypad->rotary_down_key[r]) {
- int keycode = (delta > 0) ? keypad->rotary_up_key[r] :
- keypad->rotary_down_key[r];
+ if (keypad->rotary_rel_code[r] == -1) {
+ int code = MAX_MATRIX_KEY_NUM + 2 * r + (delta > 0 ? 0 : 1);
+ unsigned char keycode = keypad->keycodes[code];
/* simulate a press-n-release */
+ input_event(dev, EV_MSC, MSC_SCAN, code);
input_report_key(dev, keycode, 1);
input_sync(dev);
+ input_event(dev, EV_MSC, MSC_SCAN, code);
input_report_key(dev, keycode, 0);
input_sync(dev);
} else {
@@ -286,6 +300,7 @@ static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad)
static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad)
{
struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+ struct input_dev *input_dev = keypad->input_dev;
unsigned int new_state;
uint32_t kpdk, bits_changed;
int i;
@@ -295,9 +310,6 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad)
if (pdata->enable_rotary0 || pdata->enable_rotary1)
pxa27x_keypad_scan_rotary(keypad);
- if (pdata->direct_key_map == NULL)
- return;
-
new_state = KPDK_DK(kpdk) & keypad->direct_key_mask;
bits_changed = keypad->direct_key_state ^ new_state;
@@ -305,12 +317,15 @@ static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad)
return;
for (i = 0; i < pdata->direct_key_num; i++) {
- if (bits_changed & (1 << i))
- input_report_key(keypad->input_dev,
- pdata->direct_key_map[i],
- (new_state & (1 << i)));
+ if (bits_changed & (1 << i)) {
+ int code = MAX_MATRIX_KEY_NUM + i;
+
+ input_event(input_dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(input_dev, keypad->keycodes[code],
+ new_state & (1 << i));
+ }
}
- input_sync(keypad->input_dev);
+ input_sync(input_dev);
keypad->direct_key_state = new_state;
}
@@ -388,8 +403,9 @@ static void pxa27x_keypad_close(struct input_dev *dev)
}
#ifdef CONFIG_PM
-static int pxa27x_keypad_suspend(struct platform_device *pdev, pm_message_t state)
+static int pxa27x_keypad_suspend(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
clk_disable(keypad->clk);
@@ -400,8 +416,9 @@ static int pxa27x_keypad_suspend(struct platform_device *pdev, pm_message_t stat
return 0;
}
-static int pxa27x_keypad_resume(struct platform_device *pdev)
+static int pxa27x_keypad_resume(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
struct input_dev *input_dev = keypad->input_dev;
@@ -420,55 +437,58 @@ static int pxa27x_keypad_resume(struct platform_device *pdev)
return 0;
}
-#else
-#define pxa27x_keypad_suspend NULL
-#define pxa27x_keypad_resume NULL
-#endif
-#define res_size(res) ((res)->end - (res)->start + 1)
+static const struct dev_pm_ops pxa27x_keypad_pm_ops = {
+ .suspend = pxa27x_keypad_suspend,
+ .resume = pxa27x_keypad_resume,
+};
+#endif
static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
{
+ struct pxa27x_keypad_platform_data *pdata = pdev->dev.platform_data;
struct pxa27x_keypad *keypad;
struct input_dev *input_dev;
struct resource *res;
int irq, error;
- keypad = kzalloc(sizeof(struct pxa27x_keypad), GFP_KERNEL);
- if (keypad == NULL) {
- dev_err(&pdev->dev, "failed to allocate driver data\n");
- return -ENOMEM;
- }
-
- keypad->pdata = pdev->dev.platform_data;
- if (keypad->pdata == NULL) {
+ if (pdata == NULL) {
dev_err(&pdev->dev, "no platform data defined\n");
- error = -EINVAL;
- goto failed_free;
+ return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get keypad irq\n");
- error = -ENXIO;
- goto failed_free;
+ return -ENXIO;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get I/O memory\n");
- error = -ENXIO;
+ return -ENXIO;
+ }
+
+ keypad = kzalloc(sizeof(struct pxa27x_keypad), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!keypad || !input_dev) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ error = -ENOMEM;
goto failed_free;
}
- res = request_mem_region(res->start, res_size(res), pdev->name);
+ keypad->pdata = pdata;
+ keypad->input_dev = input_dev;
+ keypad->irq = irq;
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
if (res == NULL) {
dev_err(&pdev->dev, "failed to request I/O memory\n");
error = -EBUSY;
goto failed_free;
}
- keypad->mmio_base = ioremap(res->start, res_size(res));
+ keypad->mmio_base = ioremap(res->start, resource_size(res));
if (keypad->mmio_base == NULL) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
error = -ENXIO;
@@ -482,43 +502,35 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
goto failed_free_io;
}
- /* Create and register the input driver. */
- input_dev = input_allocate_device();
- if (!input_dev) {
- dev_err(&pdev->dev, "failed to allocate input device\n");
- error = -ENOMEM;
- goto failed_put_clk;
- }
-
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
input_dev->open = pxa27x_keypad_open;
input_dev->close = pxa27x_keypad_close;
input_dev->dev.parent = &pdev->dev;
- keypad->input_dev = input_dev;
+ input_dev->keycode = keypad->keycodes;
+ input_dev->keycodesize = sizeof(keypad->keycodes[0]);
+ input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);
+
input_set_drvdata(input_dev, keypad);
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
- if ((keypad->pdata->enable_rotary0 &&
- keypad->pdata->rotary0_rel_code) ||
- (keypad->pdata->enable_rotary1 &&
- keypad->pdata->rotary1_rel_code)) {
- input_dev->evbit[0] |= BIT_MASK(EV_REL);
- }
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
pxa27x_keypad_build_keycode(keypad);
- platform_set_drvdata(pdev, keypad);
+
+ if ((pdata->enable_rotary0 && keypad->rotary_rel_code[0] != -1) ||
+ (pdata->enable_rotary1 && keypad->rotary_rel_code[1] != -1)) {
+ input_dev->evbit[0] |= BIT_MASK(EV_REL);
+ }
error = request_irq(irq, pxa27x_keypad_irq_handler, IRQF_DISABLED,
pdev->name, keypad);
if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n");
- goto failed_free_dev;
+ goto failed_put_clk;
}
- keypad->irq = irq;
-
/* Register the input device */
error = input_register_device(input_dev);
if (error) {
@@ -526,22 +538,21 @@ static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
goto failed_free_irq;
}
+ platform_set_drvdata(pdev, keypad);
device_init_wakeup(&pdev->dev, 1);
return 0;
failed_free_irq:
free_irq(irq, pdev);
- platform_set_drvdata(pdev, NULL);
-failed_free_dev:
- input_free_device(input_dev);
failed_put_clk:
clk_put(keypad->clk);
failed_free_io:
iounmap(keypad->mmio_base);
failed_free_mem:
- release_mem_region(res->start, res_size(res));
+ release_mem_region(res->start, resource_size(res));
failed_free:
+ input_free_device(input_dev);
kfree(keypad);
return error;
}
@@ -552,8 +563,6 @@ static int __devexit pxa27x_keypad_remove(struct platform_device *pdev)
struct resource *res;
free_irq(keypad->irq, pdev);
-
- clk_disable(keypad->clk);
clk_put(keypad->clk);
input_unregister_device(keypad->input_dev);
@@ -562,10 +571,11 @@ static int __devexit pxa27x_keypad_remove(struct platform_device *pdev)
iounmap(keypad->mmio_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, res_size(res));
+ release_mem_region(res->start, resource_size(res));
platform_set_drvdata(pdev, NULL);
kfree(keypad);
+
return 0;
}
@@ -575,11 +585,12 @@ MODULE_ALIAS("platform:pxa27x-keypad");
static struct platform_driver pxa27x_keypad_driver = {
.probe = pxa27x_keypad_probe,
.remove = __devexit_p(pxa27x_keypad_remove),
- .suspend = pxa27x_keypad_suspend,
- .resume = pxa27x_keypad_resume,
.driver = {
.name = "pxa27x-keypad",
.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &pxa27x_keypad_pm_ops,
+#endif
},
};
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
index cea70e6a103..0714bf2c28f 100644
--- a/drivers/input/keyboard/sh_keysc.c
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -128,7 +128,7 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
struct resource *res;
struct input_dev *input;
char clk_name[8];
- int i, k;
+ int i;
int irq, error;
if (!pdev->dev.platform_data) {
@@ -195,17 +195,19 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
input->id.product = 0x0001;
input->id.version = 0x0100;
+ input->keycode = pdata->keycodes;
+ input->keycodesize = sizeof(pdata->keycodes[0]);
+ input->keycodemax = ARRAY_SIZE(pdata->keycodes);
+
error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev);
if (error) {
dev_err(&pdev->dev, "failed to request IRQ\n");
goto err4;
}
- for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
- k = pdata->keycodes[i];
- if (k)
- input_set_capability(input, EV_KEY, k);
- }
+ for (i = 0; i < SH_KEYSC_MAXKEYS; i++)
+ __set_bit(pdata->keycodes[i], input->keybit);
+ __clear_bit(KEY_RESERVED, input->keybit);
error = input_register_device(input);
if (error) {
@@ -221,7 +223,9 @@ static int __devinit sh_keysc_probe(struct platform_device *pdev)
iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS);
device_init_wakeup(&pdev->dev, 1);
+
return 0;
+
err5:
free_irq(irq, pdev);
err4:
@@ -252,6 +256,7 @@ static int __devexit sh_keysc_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
kfree(priv);
+
return 0;
}
@@ -267,11 +272,12 @@ static int sh_keysc_suspend(struct device *dev)
if (device_may_wakeup(dev)) {
value |= 0x80;
enable_irq_wake(irq);
- }
- else
+ } else {
value &= ~0x80;
+ }
iowrite16(value, priv->iomem_base + KYCR1_OFFS);
+
return 0;
}
diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c
index 9fce6d1e29b..472b56639cd 100644
--- a/drivers/input/keyboard/sunkbd.c
+++ b/drivers/input/keyboard/sunkbd.c
@@ -73,7 +73,7 @@ static unsigned char sunkbd_keycode[128] = {
*/
struct sunkbd {
- unsigned char keycode[128];
+ unsigned char keycode[ARRAY_SIZE(sunkbd_keycode)];
struct input_dev *dev;
struct serio *serio;
struct work_struct tq;
@@ -81,7 +81,7 @@ struct sunkbd {
char name[64];
char phys[32];
char type;
- unsigned char enabled;
+ bool enabled;
volatile s8 reset;
volatile s8 layout;
};
@@ -94,10 +94,14 @@ struct sunkbd {
static irqreturn_t sunkbd_interrupt(struct serio *serio,
unsigned char data, unsigned int flags)
{
- struct sunkbd* sunkbd = serio_get_drvdata(serio);
+ struct sunkbd *sunkbd = serio_get_drvdata(serio);
- if (sunkbd->reset <= -1) { /* If cp[i] is 0xff, sunkbd->reset will stay -1. */
- sunkbd->reset = data; /* The keyboard sends 0xff 0xff 0xID on powerup */
+ if (sunkbd->reset <= -1) {
+ /*
+ * If cp[i] is 0xff, sunkbd->reset will stay -1.
+ * The keyboard sends 0xff 0xff 0xID on powerup.
+ */
+ sunkbd->reset = data;
wake_up_interruptible(&sunkbd->wait);
goto out;
}
@@ -110,29 +114,33 @@ static irqreturn_t sunkbd_interrupt(struct serio *serio,
switch (data) {
- case SUNKBD_RET_RESET:
- schedule_work(&sunkbd->tq);
- sunkbd->reset = -1;
- break;
+ case SUNKBD_RET_RESET:
+ schedule_work(&sunkbd->tq);
+ sunkbd->reset = -1;
+ break;
- case SUNKBD_RET_LAYOUT:
- sunkbd->layout = -1;
- break;
+ case SUNKBD_RET_LAYOUT:
+ sunkbd->layout = -1;
+ break;
+
+ case SUNKBD_RET_ALLUP: /* All keys released */
+ break;
- case SUNKBD_RET_ALLUP: /* All keys released */
+ default:
+ if (!sunkbd->enabled)
break;
- default:
- if (!sunkbd->enabled)
- break;
-
- if (sunkbd->keycode[data & SUNKBD_KEY]) {
- input_report_key(sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE));
- input_sync(sunkbd->dev);
- } else {
- printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n",
- data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed");
- }
+ if (sunkbd->keycode[data & SUNKBD_KEY]) {
+ input_report_key(sunkbd->dev,
+ sunkbd->keycode[data & SUNKBD_KEY],
+ !(data & SUNKBD_RELEASE));
+ input_sync(sunkbd->dev);
+ } else {
+ printk(KERN_WARNING
+ "sunkbd.c: Unknown key (scancode %#x) %s.\n",
+ data & SUNKBD_KEY,
+ data & SUNKBD_RELEASE ? "released" : "pressed");
+ }
}
out:
return IRQ_HANDLED;
@@ -142,34 +150,37 @@ out:
* sunkbd_event() handles events from the input module.
*/
-static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+static int sunkbd_event(struct input_dev *dev,
+ unsigned int type, unsigned int code, int value)
{
struct sunkbd *sunkbd = input_get_drvdata(dev);
switch (type) {
- case EV_LED:
+ case EV_LED:
- sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
- sunkbd->serio->write(sunkbd->serio,
- (!!test_bit(LED_CAPSL, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) |
- (!!test_bit(LED_COMPOSE, dev->led) << 1) | !!test_bit(LED_NUML, dev->led));
- return 0;
+ serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
+ serio_write(sunkbd->serio,
+ (!!test_bit(LED_CAPSL, dev->led) << 3) |
+ (!!test_bit(LED_SCROLLL, dev->led) << 2) |
+ (!!test_bit(LED_COMPOSE, dev->led) << 1) |
+ !!test_bit(LED_NUML, dev->led));
+ return 0;
- case EV_SND:
+ case EV_SND:
- switch (code) {
+ switch (code) {
- case SND_CLICK:
- sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
- return 0;
+ case SND_CLICK:
+ serio_write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
+ return 0;
- case SND_BELL:
- sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
- return 0;
- }
+ case SND_BELL:
+ serio_write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
+ return 0;
+ }
- break;
+ break;
}
return -1;
@@ -183,7 +194,7 @@ static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int c
static int sunkbd_initialize(struct sunkbd *sunkbd)
{
sunkbd->reset = -2;
- sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET);
+ serio_write(sunkbd->serio, SUNKBD_CMD_RESET);
wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
if (sunkbd->reset < 0)
return -1;
@@ -192,10 +203,13 @@ static int sunkbd_initialize(struct sunkbd *sunkbd)
if (sunkbd->type == 4) { /* Type 4 keyboard */
sunkbd->layout = -2;
- sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
- wait_event_interruptible_timeout(sunkbd->wait, sunkbd->layout >= 0, HZ/4);
- if (sunkbd->layout < 0) return -1;
- if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) sunkbd->type = 5;
+ serio_write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
+ wait_event_interruptible_timeout(sunkbd->wait,
+ sunkbd->layout >= 0, HZ / 4);
+ if (sunkbd->layout < 0)
+ return -1;
+ if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK)
+ sunkbd->type = 5;
}
return 0;
@@ -212,15 +226,19 @@ static void sunkbd_reinit(struct work_struct *work)
wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
- sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
- sunkbd->serio->write(sunkbd->serio,
- (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) | (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
- (!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) | !!test_bit(LED_NUML, sunkbd->dev->led));
- sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
- sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
+ serio_write(sunkbd->serio, SUNKBD_CMD_SETLED);
+ serio_write(sunkbd->serio,
+ (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) |
+ (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
+ (!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) |
+ !!test_bit(LED_NUML, sunkbd->dev->led));
+ serio_write(sunkbd->serio,
+ SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
+ serio_write(sunkbd->serio,
+ SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
}
-static void sunkbd_enable(struct sunkbd *sunkbd, int enable)
+static void sunkbd_enable(struct sunkbd *sunkbd, bool enable)
{
serio_pause_rx(sunkbd->serio);
sunkbd->enabled = enable;
@@ -228,7 +246,8 @@ static void sunkbd_enable(struct sunkbd *sunkbd, int enable)
}
/*
- * sunkbd_connect() probes for a Sun keyboard and fills the necessary structures.
+ * sunkbd_connect() probes for a Sun keyboard and fills the necessary
+ * structures.
*/
static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
@@ -260,7 +279,8 @@ static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
goto fail3;
}
- snprintf(sunkbd->name, sizeof(sunkbd->name), "Sun Type %d keyboard", sunkbd->type);
+ snprintf(sunkbd->name, sizeof(sunkbd->name),
+ "Sun Type %d keyboard", sunkbd->type);
memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
input_dev->name = sunkbd->name;
@@ -284,11 +304,11 @@ static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
input_dev->keycode = sunkbd->keycode;
input_dev->keycodesize = sizeof(unsigned char);
input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
- for (i = 0; i < 128; i++)
- set_bit(sunkbd->keycode[i], input_dev->keybit);
- clear_bit(0, input_dev->keybit);
+ for (i = 0; i < ARRAY_SIZE(sunkbd_keycode); i++)
+ __set_bit(sunkbd->keycode[i], input_dev->keybit);
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
- sunkbd_enable(sunkbd, 1);
+ sunkbd_enable(sunkbd, true);
err = input_register_device(sunkbd->dev);
if (err)
@@ -296,7 +316,7 @@ static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
return 0;
- fail4: sunkbd_enable(sunkbd, 0);
+ fail4: sunkbd_enable(sunkbd, false);
fail3: serio_close(serio);
fail2: serio_set_drvdata(serio, NULL);
fail1: input_free_device(input_dev);
@@ -312,7 +332,7 @@ static void sunkbd_disconnect(struct serio *serio)
{
struct sunkbd *sunkbd = serio_get_drvdata(serio);
- sunkbd_enable(sunkbd, 0);
+ sunkbd_enable(sunkbd, false);
input_unregister_device(sunkbd->dev);
serio_close(serio);
serio_set_drvdata(serio, NULL);
diff --git a/drivers/input/keyboard/tosakbd.c b/drivers/input/keyboard/tosakbd.c
index 677276b1202..42cb3faf733 100644
--- a/drivers/input/keyboard/tosakbd.c
+++ b/drivers/input/keyboard/tosakbd.c
@@ -31,7 +31,7 @@
#define KB_DISCHARGE_DELAY 10
#define KB_ACTIVATE_DELAY 10
-static unsigned int tosakbd_keycode[NR_SCANCODES] = {
+static unsigned short tosakbd_keycode[NR_SCANCODES] = {
0,
0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -50,9 +50,9 @@ KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_
};
struct tosakbd {
- unsigned int keycode[ARRAY_SIZE(tosakbd_keycode)];
+ unsigned short keycode[ARRAY_SIZE(tosakbd_keycode)];
struct input_dev *input;
- int suspended;
+ bool suspended;
spinlock_t lock; /* protect kbd scanning */
struct timer_list timer;
};
@@ -215,7 +215,7 @@ static int tosakbd_suspend(struct platform_device *dev, pm_message_t state)
unsigned long flags;
spin_lock_irqsave(&tosakbd->lock, flags);
- tosakbd->suspended = 1;
+ tosakbd->suspended = true;
spin_unlock_irqrestore(&tosakbd->lock, flags);
del_timer_sync(&tosakbd->timer);
@@ -227,7 +227,7 @@ static int tosakbd_resume(struct platform_device *dev)
{
struct tosakbd *tosakbd = platform_get_drvdata(dev);
- tosakbd->suspended = 0;
+ tosakbd->suspended = false;
tosakbd_scankeyboard(dev);
return 0;
@@ -277,14 +277,14 @@ static int __devinit tosakbd_probe(struct platform_device *pdev) {
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
input_dev->keycode = tosakbd->keycode;
- input_dev->keycodesize = sizeof(unsigned int);
+ input_dev->keycodesize = sizeof(tosakbd->keycode[0]);
input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode);
memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd_keycode));
for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++)
__set_bit(tosakbd->keycode[i], input_dev->keybit);
- clear_bit(0, input_dev->keybit);
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
/* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) {
@@ -344,7 +344,7 @@ static int __devinit tosakbd_probe(struct platform_device *pdev) {
" direction for GPIO %d, error %d\n",
gpio, error);
gpio_free(gpio);
- goto fail;
+ goto fail2;
}
}
@@ -353,7 +353,7 @@ static int __devinit tosakbd_probe(struct platform_device *pdev) {
if (error) {
printk(KERN_ERR "tosakbd: Unable to register input device, "
"error: %d\n", error);
- goto fail;
+ goto fail2;
}
printk(KERN_INFO "input: Tosa Keyboard Registered\n");
diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
new file mode 100644
index 00000000000..9a2977c2169
--- /dev/null
+++ b/drivers/input/keyboard/twl4030_keypad.c
@@ -0,0 +1,480 @@
+/*
+ * twl4030_keypad.c - driver for 8x8 keypad controller in twl4030 chips
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Code re-written for 2430SDP by:
+ * Syed Mohammed Khasim <x0khasim@ti.com>
+ *
+ * Initial Code:
+ * Manjunatha G K <manjugk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+
+
+/*
+ * The TWL4030 family chips include a keypad controller that supports
+ * up to an 8x8 switch matrix. The controller can issue system wakeup
+ * events, since it uses only the always-on 32KiHz oscillator, and has
+ * an internal state machine that decodes pressed keys, including
+ * multi-key combinations.
+ *
+ * This driver lets boards define what keycodes they wish to report for
+ * which scancodes, as part of the "struct twl4030_keypad_data" used in
+ * the probe() routine.
+ *
+ * See the TPS65950 documentation; that's the general availability
+ * version of the TWL5030 second generation part.
+ */
+#define TWL4030_MAX_ROWS 8 /* TWL4030 hard limit */
+#define TWL4030_MAX_COLS 8
+#define TWL4030_ROW_SHIFT 3
+#define TWL4030_KEYMAP_SIZE (TWL4030_MAX_ROWS * TWL4030_MAX_COLS)
+
+struct twl4030_keypad {
+ unsigned short keymap[TWL4030_KEYMAP_SIZE];
+ u16 kp_state[TWL4030_MAX_ROWS];
+ unsigned n_rows;
+ unsigned n_cols;
+ unsigned irq;
+
+ struct device *dbg_dev;
+ struct input_dev *input;
+};
+
+/*----------------------------------------------------------------------*/
+
+/* arbitrary prescaler value 0..7 */
+#define PTV_PRESCALER 4
+
+/* Register Offsets */
+#define KEYP_CTRL 0x00
+#define KEYP_DEB 0x01
+#define KEYP_LONG_KEY 0x02
+#define KEYP_LK_PTV 0x03
+#define KEYP_TIMEOUT_L 0x04
+#define KEYP_TIMEOUT_H 0x05
+#define KEYP_KBC 0x06
+#define KEYP_KBR 0x07
+#define KEYP_SMS 0x08
+#define KEYP_FULL_CODE_7_0 0x09 /* row 0 column status */
+#define KEYP_FULL_CODE_15_8 0x0a /* ... row 1 ... */
+#define KEYP_FULL_CODE_23_16 0x0b
+#define KEYP_FULL_CODE_31_24 0x0c
+#define KEYP_FULL_CODE_39_32 0x0d
+#define KEYP_FULL_CODE_47_40 0x0e
+#define KEYP_FULL_CODE_55_48 0x0f
+#define KEYP_FULL_CODE_63_56 0x10
+#define KEYP_ISR1 0x11
+#define KEYP_IMR1 0x12
+#define KEYP_ISR2 0x13
+#define KEYP_IMR2 0x14
+#define KEYP_SIR 0x15
+#define KEYP_EDR 0x16 /* edge triggers */
+#define KEYP_SIH_CTRL 0x17
+
+/* KEYP_CTRL_REG Fields */
+#define KEYP_CTRL_SOFT_NRST BIT(0)
+#define KEYP_CTRL_SOFTMODEN BIT(1)
+#define KEYP_CTRL_LK_EN BIT(2)
+#define KEYP_CTRL_TOE_EN BIT(3)
+#define KEYP_CTRL_TOLE_EN BIT(4)
+#define KEYP_CTRL_RP_EN BIT(5)
+#define KEYP_CTRL_KBD_ON BIT(6)
+
+/* KEYP_DEB, KEYP_LONG_KEY, KEYP_TIMEOUT_x*/
+#define KEYP_PERIOD_US(t, prescale) ((t) / (31 << (prescale + 1)) - 1)
+
+/* KEYP_LK_PTV_REG Fields */
+#define KEYP_LK_PTV_PTV_SHIFT 5
+
+/* KEYP_{IMR,ISR,SIR} Fields */
+#define KEYP_IMR1_MIS BIT(3)
+#define KEYP_IMR1_TO BIT(2)
+#define KEYP_IMR1_LK BIT(1)
+#define KEYP_IMR1_KP BIT(0)
+
+/* KEYP_EDR Fields */
+#define KEYP_EDR_KP_FALLING 0x01
+#define KEYP_EDR_KP_RISING 0x02
+#define KEYP_EDR_KP_BOTH 0x03
+#define KEYP_EDR_LK_FALLING 0x04
+#define KEYP_EDR_LK_RISING 0x08
+#define KEYP_EDR_TO_FALLING 0x10
+#define KEYP_EDR_TO_RISING 0x20
+#define KEYP_EDR_MIS_FALLING 0x40
+#define KEYP_EDR_MIS_RISING 0x80
+
+
+/*----------------------------------------------------------------------*/
+
+static int twl4030_kpread(struct twl4030_keypad *kp,
+ u8 *data, u32 reg, u8 num_bytes)
+{
+ int ret = twl4030_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
+
+ if (ret < 0)
+ dev_warn(kp->dbg_dev,
+ "Couldn't read TWL4030: %X - ret %d[%x]\n",
+ reg, ret, ret);
+
+ return ret;
+}
+
+static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
+{
+ int ret = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
+
+ if (ret < 0)
+ dev_warn(kp->dbg_dev,
+ "Could not write TWL4030: %X - ret %d[%x]\n",
+ reg, ret, ret);
+
+ return ret;
+}
+
+static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
+{
+ /* If all bits in a row are active for all coloumns then
+ * we have that row line connected to gnd. Mark this
+ * key on as if it was on matrix position n_cols (ie
+ * one higher than the size of the matrix).
+ */
+ if (col == 0xFF)
+ return 1 << kp->n_cols;
+ else
+ return col & ((1 << kp->n_cols) - 1);
+}
+
+static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state)
+{
+ u8 new_state[TWL4030_MAX_ROWS];
+ int row;
+ int ret = twl4030_kpread(kp, new_state,
+ KEYP_FULL_CODE_7_0, kp->n_rows);
+ if (ret >= 0)
+ for (row = 0; row < kp->n_rows; row++)
+ state[row] = twl4030_col_xlate(kp, new_state[row]);
+
+ return ret;
+}
+
+static int twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state)
+{
+ int i;
+ u16 check = 0;
+
+ for (i = 0; i < kp->n_rows; i++) {
+ u16 col = key_state[i];
+
+ if ((col & check) && hweight16(col) > 1)
+ return 1;
+
+ check |= col;
+ }
+
+ return 0;
+}
+
+static void twl4030_kp_scan(struct twl4030_keypad *kp, bool release_all)
+{
+ struct input_dev *input = kp->input;
+ u16 new_state[TWL4030_MAX_ROWS];
+ int col, row;
+
+ if (release_all)
+ memset(new_state, 0, sizeof(new_state));
+ else {
+ /* check for any changes */
+ int ret = twl4030_read_kp_matrix_state(kp, new_state);
+
+ if (ret < 0) /* panic ... */
+ return;
+
+ if (twl4030_is_in_ghost_state(kp, new_state))
+ return;
+ }
+
+ /* check for changes and print those */
+ for (row = 0; row < kp->n_rows; row++) {
+ int changed = new_state[row] ^ kp->kp_state[row];
+
+ if (!changed)
+ continue;
+
+ for (col = 0; col < kp->n_cols; col++) {
+ int code;
+
+ if (!(changed & (1 << col)))
+ continue;
+
+ dev_dbg(kp->dbg_dev, "key [%d:%d] %s\n", row, col,
+ (new_state[row] & (1 << col)) ?
+ "press" : "release");
+
+ code = MATRIX_SCAN_CODE(row, col, TWL4030_ROW_SHIFT);
+ input_event(input, EV_MSC, MSC_SCAN, code);
+ input_report_key(input, kp->keymap[code],
+ new_state[row] & (1 << col));
+ }
+ kp->kp_state[row] = new_state[row];
+ }
+ input_sync(input);
+}
+
+/*
+ * Keypad interrupt handler
+ */
+static irqreturn_t do_kp_irq(int irq, void *_kp)
+{
+ struct twl4030_keypad *kp = _kp;
+ u8 reg;
+ int ret;
+
+#ifdef CONFIG_LOCKDEP
+ /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
+ * we don't want and can't tolerate. Although it might be
+ * friendlier not to borrow this thread context...
+ */
+ local_irq_enable();
+#endif
+
+ /* Read & Clear TWL4030 pending interrupt */
+ ret = twl4030_kpread(kp, &reg, KEYP_ISR1, 1);
+
+ /* Release all keys if I2C has gone bad or
+ * the KEYP has gone to idle state */
+ if (ret >= 0 && (reg & KEYP_IMR1_KP))
+ twl4030_kp_scan(kp, false);
+ else
+ twl4030_kp_scan(kp, true);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit twl4030_kp_program(struct twl4030_keypad *kp)
+{
+ u8 reg;
+ int i;
+
+ /* Enable controller, with hardware decoding but not autorepeat */
+ reg = KEYP_CTRL_SOFT_NRST | KEYP_CTRL_SOFTMODEN
+ | KEYP_CTRL_TOE_EN | KEYP_CTRL_KBD_ON;
+ if (twl4030_kpwrite_u8(kp, reg, KEYP_CTRL) < 0)
+ return -EIO;
+
+ /* NOTE: we could use sih_setup() here to package keypad
+ * event sources as four different IRQs ... but we don't.
+ */
+
+ /* Enable TO rising and KP rising and falling edge detection */
+ reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING;
+ if (twl4030_kpwrite_u8(kp, reg, KEYP_EDR) < 0)
+ return -EIO;
+
+ /* Set PTV prescaler Field */
+ reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT);
+ if (twl4030_kpwrite_u8(kp, reg, KEYP_LK_PTV) < 0)
+ return -EIO;
+
+ /* Set key debounce time to 20 ms */
+ i = KEYP_PERIOD_US(20000, PTV_PRESCALER);
+ if (twl4030_kpwrite_u8(kp, i, KEYP_DEB) < 0)
+ return -EIO;
+
+ /* Set timeout period to 100 ms */
+ i = KEYP_PERIOD_US(200000, PTV_PRESCALER);
+ if (twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L) < 0)
+ return -EIO;
+
+ if (twl4030_kpwrite_u8(kp, (i >> 8), KEYP_TIMEOUT_H) < 0)
+ return -EIO;
+
+ /*
+ * Enable Clear-on-Read; disable remembering events that fire
+ * after the IRQ but before our handler acks (reads) them,
+ */
+ reg = TWL4030_SIH_CTRL_COR_MASK | TWL4030_SIH_CTRL_PENDDIS_MASK;
+ if (twl4030_kpwrite_u8(kp, reg, KEYP_SIH_CTRL) < 0)
+ return -EIO;
+
+ /* initialize key state; irqs update it from here on */
+ if (twl4030_read_kp_matrix_state(kp, kp->kp_state) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Registers keypad device with input subsystem
+ * and configures TWL4030 keypad registers
+ */
+static int __devinit twl4030_kp_probe(struct platform_device *pdev)
+{
+ struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
+ const struct matrix_keymap_data *keymap_data = pdata->keymap_data;
+ struct twl4030_keypad *kp;
+ struct input_dev *input;
+ u8 reg;
+ int error;
+
+ if (!pdata || !pdata->rows || !pdata->cols ||
+ pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) {
+ dev_err(&pdev->dev, "Invalid platform_data\n");
+ return -EINVAL;
+ }
+
+ kp = kzalloc(sizeof(*kp), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!kp || !input) {
+ error = -ENOMEM;
+ goto err1;
+ }
+
+ /* Get the debug Device */
+ kp->dbg_dev = &pdev->dev;
+ kp->input = input;
+
+ kp->n_rows = pdata->rows;
+ kp->n_cols = pdata->cols;
+ kp->irq = platform_get_irq(pdev, 0);
+
+ /* setup input device */
+ __set_bit(EV_KEY, input->evbit);
+
+ /* Enable auto repeat feature of Linux input subsystem */
+ if (pdata->rep)
+ __set_bit(EV_REP, input->evbit);
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+
+ input->name = "TWL4030 Keypad";
+ input->phys = "twl4030_keypad/input0";
+ input->dev.parent = &pdev->dev;
+
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = 0x0003;
+
+ input->keycode = kp->keymap;
+ input->keycodesize = sizeof(kp->keymap[0]);
+ input->keycodemax = ARRAY_SIZE(kp->keymap);
+
+ matrix_keypad_build_keymap(keymap_data, TWL4030_ROW_SHIFT,
+ input->keycode, input->keybit);
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(kp->dbg_dev,
+ "Unable to register twl4030 keypad device\n");
+ goto err1;
+ }
+
+ error = twl4030_kp_program(kp);
+ if (error)
+ goto err2;
+
+ /*
+ * This ISR will always execute in kernel thread context because of
+ * the need to access the TWL4030 over the I2C bus.
+ *
+ * NOTE: we assume this host is wired to TWL4040 INT1, not INT2 ...
+ */
+ error = request_irq(kp->irq, do_kp_irq, 0, pdev->name, kp);
+ if (error) {
+ dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
+ kp->irq);
+ goto err3;
+ }
+
+ /* Enable KP and TO interrupts now. */
+ reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
+ if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) {
+ error = -EIO;
+ goto err4;
+ }
+
+ platform_set_drvdata(pdev, kp);
+ return 0;
+
+err4:
+ /* mask all events - we don't care about the result */
+ (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
+err3:
+ free_irq(kp->irq, NULL);
+err2:
+ input_unregister_device(input);
+ input = NULL;
+err1:
+ input_free_device(input);
+ kfree(kp);
+ return error;
+}
+
+static int __devexit twl4030_kp_remove(struct platform_device *pdev)
+{
+ struct twl4030_keypad *kp = platform_get_drvdata(pdev);
+
+ free_irq(kp->irq, kp);
+ input_unregister_device(kp->input);
+ platform_set_drvdata(pdev, NULL);
+ kfree(kp);
+
+ return 0;
+}
+
+/*
+ * NOTE: twl4030 are multi-function devices connected via I2C.
+ * So this device is a child of an I2C parent, thus it needs to
+ * support unplug/replug (which most platform devices don't).
+ */
+
+static struct platform_driver twl4030_kp_driver = {
+ .probe = twl4030_kp_probe,
+ .remove = __devexit_p(twl4030_kp_remove),
+ .driver = {
+ .name = "twl4030_keypad",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init twl4030_kp_init(void)
+{
+ return platform_driver_register(&twl4030_kp_driver);
+}
+module_init(twl4030_kp_init);
+
+static void __exit twl4030_kp_exit(void)
+{
+ platform_driver_unregister(&twl4030_kp_driver);
+}
+module_exit(twl4030_kp_exit);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("TWL4030 Keypad Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:twl4030_keypad");
+
diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c
new file mode 100644
index 00000000000..6032def0370
--- /dev/null
+++ b/drivers/input/keyboard/w90p910_keypad.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2008-2009 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation;version 2 of the License.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <mach/w90p910_keypad.h>
+
+/* Keypad Interface Control Registers */
+#define KPI_CONF 0x00
+#define KPI_3KCONF 0x04
+#define KPI_LPCONF 0x08
+#define KPI_STATUS 0x0C
+
+#define IS1KEY (0x01 << 16)
+#define INTTR (0x01 << 21)
+#define KEY0R (0x0f << 3)
+#define KEY0C 0x07
+#define DEBOUNCE_BIT 0x08
+#define KSIZE0 (0x01 << 16)
+#define KSIZE1 (0x01 << 17)
+#define KPSEL (0x01 << 19)
+#define ENKP (0x01 << 18)
+
+#define KGET_RAW(n) (((n) & KEY0R) >> 3)
+#define KGET_COLUMN(n) ((n) & KEY0C)
+
+#define W90P910_MAX_KEY_NUM (8 * 8)
+#define W90P910_ROW_SHIFT 3
+
+struct w90p910_keypad {
+ const struct w90p910_keypad_platform_data *pdata;
+ struct clk *clk;
+ struct input_dev *input_dev;
+ void __iomem *mmio_base;
+ int irq;
+ unsigned short keymap[W90P910_MAX_KEY_NUM];
+};
+
+static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad,
+ unsigned int status)
+{
+ struct input_dev *input_dev = keypad->input_dev;
+ unsigned int row = KGET_RAW(status);
+ unsigned int col = KGET_COLUMN(status);
+ unsigned int code = MATRIX_SCAN_CODE(row, col, W90P910_ROW_SHIFT);
+ unsigned int key = keypad->keymap[code];
+
+ input_event(input_dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(input_dev, key, 1);
+ input_sync(input_dev);
+
+ input_event(input_dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(input_dev, key, 0);
+ input_sync(input_dev);
+}
+
+static irqreturn_t w90p910_keypad_irq_handler(int irq, void *dev_id)
+{
+ struct w90p910_keypad *keypad = dev_id;
+ unsigned int kstatus, val;
+
+ kstatus = __raw_readl(keypad->mmio_base + KPI_STATUS);
+
+ val = INTTR | IS1KEY;
+
+ if (kstatus & val)
+ w90p910_keypad_scan_matrix(keypad, kstatus);
+
+ return IRQ_HANDLED;
+}
+
+static int w90p910_keypad_open(struct input_dev *dev)
+{
+ struct w90p910_keypad *keypad = input_get_drvdata(dev);
+ const struct w90p910_keypad_platform_data *pdata = keypad->pdata;
+ unsigned int val, config;
+
+ /* Enable unit clock */
+ clk_enable(keypad->clk);
+
+ val = __raw_readl(keypad->mmio_base + KPI_CONF);
+ val |= (KPSEL | ENKP);
+ val &= ~(KSIZE0 | KSIZE1);
+
+ config = pdata->prescale | (pdata->debounce << DEBOUNCE_BIT);
+
+ val |= config;
+
+ __raw_writel(val, keypad->mmio_base + KPI_CONF);
+
+ return 0;
+}
+
+static void w90p910_keypad_close(struct input_dev *dev)
+{
+ struct w90p910_keypad *keypad = input_get_drvdata(dev);
+
+ /* Disable clock unit */
+ clk_disable(keypad->clk);
+}
+
+static int __devinit w90p910_keypad_probe(struct platform_device *pdev)
+{
+ const struct w90p910_keypad_platform_data *pdata =
+ pdev->dev.platform_data;
+ const struct matrix_keymap_data *keymap_data;
+ struct w90p910_keypad *keypad;
+ struct input_dev *input_dev;
+ struct resource *res;
+ int irq;
+ int error;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ return -EINVAL;
+ }
+
+ keymap_data = pdata->keymap_data;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get keypad irq\n");
+ return -ENXIO;
+ }
+
+ keypad = kzalloc(sizeof(struct w90p910_keypad), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!keypad || !input_dev) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ error = -ENOMEM;
+ goto failed_free;
+ }
+
+ keypad->pdata = pdata;
+ keypad->input_dev = input_dev;
+ keypad->irq = irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get I/O memory\n");
+ error = -ENXIO;
+ goto failed_free;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ error = -EBUSY;
+ goto failed_free;
+ }
+
+ keypad->mmio_base = ioremap(res->start, resource_size(res));
+ if (keypad->mmio_base == NULL) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ error = -ENXIO;
+ goto failed_free_res;
+ }
+
+ keypad->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(keypad->clk)) {
+ dev_err(&pdev->dev, "failed to get keypad clock\n");
+ error = PTR_ERR(keypad->clk);
+ goto failed_free_io;
+ }
+
+ /* set multi-function pin for w90p910 kpi. */
+ mfp_set_groupi(&pdev->dev);
+
+ input_dev->name = pdev->name;
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->open = w90p910_keypad_open;
+ input_dev->close = w90p910_keypad_close;
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->keycode = keypad->keymap;
+ input_dev->keycodesize = sizeof(keypad->keymap[0]);
+ input_dev->keycodemax = ARRAY_SIZE(keypad->keymap);
+
+ input_set_drvdata(input_dev, keypad);
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+ matrix_keypad_build_keymap(keymap_data, W90P910_ROW_SHIFT,
+ input_dev->keycode, input_dev->keybit);
+
+ error = request_irq(keypad->irq, w90p910_keypad_irq_handler,
+ IRQF_DISABLED, pdev->name, keypad);
+ if (error) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ goto failed_put_clk;
+ }
+
+ /* Register the input device */
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ goto failed_free_irq;
+ }
+
+ platform_set_drvdata(pdev, keypad);
+ return 0;
+
+failed_free_irq:
+ free_irq(irq, pdev);
+failed_put_clk:
+ clk_put(keypad->clk);
+failed_free_io:
+ iounmap(keypad->mmio_base);
+failed_free_res:
+ release_mem_region(res->start, resource_size(res));
+failed_free:
+ input_free_device(input_dev);
+ kfree(keypad);
+ return error;
+}
+
+static int __devexit w90p910_keypad_remove(struct platform_device *pdev)
+{
+ struct w90p910_keypad *keypad = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ free_irq(keypad->irq, pdev);
+
+ clk_put(keypad->clk);
+
+ input_unregister_device(keypad->input_dev);
+
+ iounmap(keypad->mmio_base);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, resource_size(res));
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(keypad);
+
+ return 0;
+}
+
+static struct platform_driver w90p910_keypad_driver = {
+ .probe = w90p910_keypad_probe,
+ .remove = __devexit_p(w90p910_keypad_remove),
+ .driver = {
+ .name = "nuc900-keypad",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init w90p910_keypad_init(void)
+{
+ return platform_driver_register(&w90p910_keypad_driver);
+}
+
+static void __exit w90p910_keypad_exit(void)
+{
+ platform_driver_unregister(&w90p910_keypad_driver);
+}
+
+module_init(w90p910_keypad_init);
+module_exit(w90p910_keypad_exit);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("w90p910 keypad driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:nuc900-keypad");
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 1acfa3a05aa..cbe21bc96b5 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -269,4 +269,14 @@ config INPUT_DM355EVM
To compile this driver as a module, choose M here: the
module will be called dm355evm_keys.
+
+config INPUT_BFIN_ROTARY
+ tristate "Blackfin Rotary support"
+ depends on BF54x || BF52x
+ help
+ Say Y here if you want to use the Blackfin Rotary.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin-rotary.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 0d979fd4cd5..79c1e9a5ea3 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_INPUT_APANEL) += apanel.o
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
+obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c
new file mode 100644
index 00000000000..690f3fafa03
--- /dev/null
+++ b/drivers/input/misc/bfin_rotary.c
@@ -0,0 +1,283 @@
+/*
+ * Rotary counter driver for Analog Devices Blackfin Processors
+ *
+ * Copyright 2008-2009 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+
+#include <asm/portmux.h>
+#include <asm/bfin_rotary.h>
+
+static const u16 per_cnt[] = {
+ P_CNT_CUD,
+ P_CNT_CDG,
+ P_CNT_CZM,
+ 0
+};
+
+struct bfin_rot {
+ struct input_dev *input;
+ int irq;
+ unsigned int up_key;
+ unsigned int down_key;
+ unsigned int button_key;
+ unsigned int rel_code;
+ unsigned short cnt_config;
+ unsigned short cnt_imask;
+ unsigned short cnt_debounce;
+};
+
+static void report_key_event(struct input_dev *input, int keycode)
+{
+ /* simulate a press-n-release */
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+ input_report_key(input, keycode, 0);
+ input_sync(input);
+}
+
+static void report_rotary_event(struct bfin_rot *rotary, int delta)
+{
+ struct input_dev *input = rotary->input;
+
+ if (rotary->up_key) {
+ report_key_event(input,
+ delta > 0 ? rotary->up_key : rotary->down_key);
+ } else {
+ input_report_rel(input, rotary->rel_code, delta);
+ input_sync(input);
+ }
+}
+
+static irqreturn_t bfin_rotary_isr(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct bfin_rot *rotary = platform_get_drvdata(pdev);
+ int delta;
+
+ switch (bfin_read_CNT_STATUS()) {
+
+ case ICII:
+ break;
+
+ case UCII:
+ case DCII:
+ delta = bfin_read_CNT_COUNTER();
+ if (delta)
+ report_rotary_event(rotary, delta);
+ break;
+
+ case CZMII:
+ report_key_event(rotary->input, rotary->button_key);
+ break;
+
+ default:
+ break;
+ }
+
+ bfin_write_CNT_COMMAND(W1LCNT_ZERO); /* Clear COUNTER */
+ bfin_write_CNT_STATUS(-1); /* Clear STATUS */
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit bfin_rotary_probe(struct platform_device *pdev)
+{
+ struct bfin_rotary_platform_data *pdata = pdev->dev.platform_data;
+ struct bfin_rot *rotary;
+ struct input_dev *input;
+ int error;
+
+ /* Basic validation */
+ if ((pdata->rotary_up_key && !pdata->rotary_down_key) ||
+ (!pdata->rotary_up_key && pdata->rotary_down_key)) {
+ return -EINVAL;
+ }
+
+ error = peripheral_request_list(per_cnt, dev_name(&pdev->dev));
+ if (error) {
+ dev_err(&pdev->dev, "requesting peripherals failed\n");
+ return error;
+ }
+
+ rotary = kzalloc(sizeof(struct bfin_rot), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!rotary || !input) {
+ error = -ENOMEM;
+ goto out1;
+ }
+
+ rotary->input = input;
+
+ rotary->up_key = pdata->rotary_up_key;
+ rotary->down_key = pdata->rotary_down_key;
+ rotary->button_key = pdata->rotary_button_key;
+ rotary->rel_code = pdata->rotary_rel_code;
+
+ error = rotary->irq = platform_get_irq(pdev, 0);
+ if (error < 0)
+ goto out1;
+
+ input->name = pdev->name;
+ input->phys = "bfin-rotary/input0";
+ input->dev.parent = &pdev->dev;
+
+ input_set_drvdata(input, rotary);
+
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = 0x0100;
+
+ if (rotary->up_key) {
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(rotary->up_key, input->keybit);
+ __set_bit(rotary->down_key, input->keybit);
+ } else {
+ __set_bit(EV_REL, input->evbit);
+ __set_bit(rotary->rel_code, input->relbit);
+ }
+
+ if (rotary->button_key) {
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(rotary->button_key, input->keybit);
+ }
+
+ error = request_irq(rotary->irq, bfin_rotary_isr,
+ 0, dev_name(&pdev->dev), pdev);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to claim irq %d; error %d\n",
+ rotary->irq, error);
+ goto out1;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to register input device (%d)\n", error);
+ goto out2;
+ }
+
+ if (pdata->rotary_button_key)
+ bfin_write_CNT_IMASK(CZMIE);
+
+ if (pdata->mode & ROT_DEBE)
+ bfin_write_CNT_DEBOUNCE(pdata->debounce & DPRESCALE);
+
+ if (pdata->mode)
+ bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() |
+ (pdata->mode & ~CNTE));
+
+ bfin_write_CNT_IMASK(bfin_read_CNT_IMASK() | UCIE | DCIE);
+ bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | CNTE);
+
+ platform_set_drvdata(pdev, rotary);
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+out2:
+ free_irq(rotary->irq, pdev);
+out1:
+ input_free_device(input);
+ kfree(rotary);
+ peripheral_free_list(per_cnt);
+
+ return error;
+}
+
+static int __devexit bfin_rotary_remove(struct platform_device *pdev)
+{
+ struct bfin_rot *rotary = platform_get_drvdata(pdev);
+
+ bfin_write_CNT_CONFIG(0);
+ bfin_write_CNT_IMASK(0);
+
+ free_irq(rotary->irq, pdev);
+ input_unregister_device(rotary->input);
+ peripheral_free_list(per_cnt);
+
+ kfree(rotary);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bfin_rotary_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bfin_rot *rotary = platform_get_drvdata(pdev);
+
+ rotary->cnt_config = bfin_read_CNT_CONFIG();
+ rotary->cnt_imask = bfin_read_CNT_IMASK();
+ rotary->cnt_debounce = bfin_read_CNT_DEBOUNCE();
+
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(rotary->irq);
+
+ return 0;
+}
+
+static int bfin_rotary_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bfin_rot *rotary = platform_get_drvdata(pdev);
+
+ bfin_write_CNT_DEBOUNCE(rotary->cnt_debounce);
+ bfin_write_CNT_IMASK(rotary->cnt_imask);
+ bfin_write_CNT_CONFIG(rotary->cnt_config & ~CNTE);
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(rotary->irq);
+
+ if (rotary->cnt_config & CNTE)
+ bfin_write_CNT_CONFIG(rotary->cnt_config);
+
+ return 0;
+}
+
+static struct dev_pm_ops bfin_rotary_pm_ops = {
+ .suspend = bfin_rotary_suspend,
+ .resume = bfin_rotary_resume,
+};
+#endif
+
+static struct platform_driver bfin_rotary_device_driver = {
+ .probe = bfin_rotary_probe,
+ .remove = __devexit_p(bfin_rotary_remove),
+ .driver = {
+ .name = "bfin-rotary",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &bfin_rotary_pm_ops,
+#endif
+ },
+};
+
+static int __init bfin_rotary_init(void)
+{
+ return platform_driver_register(&bfin_rotary_device_driver);
+}
+module_init(bfin_rotary_init);
+
+static void __exit bfin_rotary_exit(void)
+{
+ platform_driver_unregister(&bfin_rotary_device_driver);
+}
+module_exit(bfin_rotary_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors");
+MODULE_ALIAS("platform:bfin-rotary");
diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c
index d114d3a9e1e..ee73d7219c9 100644
--- a/drivers/input/misc/cobalt_btns.c
+++ b/drivers/input/misc/cobalt_btns.c
@@ -116,7 +116,7 @@ static int __devinit cobalt_buttons_probe(struct platform_device *pdev)
}
bdev->poll_dev = poll_dev;
- bdev->reg = ioremap(res->start, res->end - res->start + 1);
+ bdev->reg = ioremap(res->start, resource_size(res));
dev_set_drvdata(&pdev->dev, bdev);
error = input_register_polled_device(poll_dev);
diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c
index a63315ce4a6..0918acae584 100644
--- a/drivers/input/misc/dm355evm_keys.c
+++ b/drivers/input/misc/dm355evm_keys.c
@@ -23,30 +23,16 @@
* pressed, or its autorepeat kicks in, an event is sent. This driver
* read those events from the small (32 event) queue and reports them.
*
- * Because we communicate with the MSP430 using I2C, and all I2C calls
- * in Linux sleep, we need to cons up a kind of threaded IRQ handler
- * using a work_struct. The IRQ is active low, but we use it through
- * the GPIO controller so we can trigger on falling edges.
- *
* Note that physically there can only be one of these devices.
*
* This driver was tested with firmware revision A4.
*/
struct dm355evm_keys {
- struct work_struct work;
struct input_dev *input;
struct device *dev;
int irq;
};
-static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
-{
- struct dm355evm_keys *keys = _keys;
-
- schedule_work(&keys->work);
- return IRQ_HANDLED;
-}
-
/* These initial keycodes can be remapped by dm355evm_setkeycode(). */
static struct {
u16 event;
@@ -110,13 +96,12 @@ static struct {
{ 0x3169, KEY_PAUSE, },
};
-static void dm355evm_keys_work(struct work_struct *work)
+/* runs in an IRQ thread -- can (and will!) sleep */
+static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
{
- struct dm355evm_keys *keys;
+ struct dm355evm_keys *keys = _keys;
int status;
- keys = container_of(work, struct dm355evm_keys, work);
-
/* For simplicity we ignore INPUT_COUNT and just read
* events until we get the "queue empty" indicator.
* Reading INPUT_LOW decrements the count.
@@ -183,6 +168,19 @@ static void dm355evm_keys_work(struct work_struct *work)
input_report_key(keys->input, keycode, 0);
input_sync(keys->input);
}
+ return IRQ_HANDLED;
+}
+
+/*
+ * Because we communicate with the MSP430 using I2C, and all I2C calls
+ * in Linux sleep, we use a threaded IRQ handler. The IRQ itself is
+ * active low, but we go through the GPIO controller so we can trigger
+ * on falling edges and not worry about enabling/disabling the IRQ in
+ * the keypress handling path.
+ */
+static irqreturn_t dm355evm_keys_hardirq(int irq, void *_keys)
+{
+ return IRQ_WAKE_THREAD;
}
static int dm355evm_setkeycode(struct input_dev *dev, int index, int keycode)
@@ -233,7 +231,6 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev)
keys->dev = &pdev->dev;
keys->input = input;
- INIT_WORK(&keys->work, dm355evm_keys_work);
/* set up "threaded IRQ handler" */
status = platform_get_irq(pdev, 0);
@@ -260,9 +257,10 @@ static int __devinit dm355evm_keys_probe(struct platform_device *pdev)
/* REVISIT: flush the event queue? */
- status = request_irq(keys->irq, dm355evm_keys_irq,
- IRQF_TRIGGER_FALLING,
- dev_name(&pdev->dev), keys);
+ status = request_threaded_irq(keys->irq,
+ dm355evm_keys_hardirq, dm355evm_keys_irq,
+ IRQF_TRIGGER_FALLING,
+ dev_name(&pdev->dev), keys);
if (status < 0)
goto fail1;
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
index 27ee976eb54..11fd038a078 100644
--- a/drivers/input/misc/wistron_btns.c
+++ b/drivers/input/misc/wistron_btns.c
@@ -243,9 +243,9 @@ enum { KE_END, KE_KEY, KE_SW, KE_WIFI, KE_BLUETOOTH };
#define FE_UNTESTED 0x80
static struct key_entry *keymap; /* = NULL; Current key map */
-static int have_wifi;
-static int have_bluetooth;
-static int have_leds;
+static bool have_wifi;
+static bool have_bluetooth;
+static int leds_present; /* bitmask of leds present */
static int __init dmi_matched(const struct dmi_system_id *dmi)
{
@@ -254,11 +254,11 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
keymap = dmi->driver_data;
for (key = keymap; key->type != KE_END; key++) {
if (key->type == KE_WIFI)
- have_wifi = 1;
+ have_wifi = true;
else if (key->type == KE_BLUETOOTH)
- have_bluetooth = 1;
+ have_bluetooth = true;
}
- have_leds = key->code & (FE_MAIL_LED | FE_WIFI_LED);
+ leds_present = key->code & (FE_MAIL_LED | FE_WIFI_LED);
return 1;
}
@@ -611,10 +611,24 @@ static struct key_entry keymap_wistron_generic[] __initdata = {
{ KE_END, 0 }
};
+static struct key_entry keymap_aopen_1557[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, 0 }
+};
+
static struct key_entry keymap_prestigio[] __initdata = {
{ KE_KEY, 0x11, {KEY_PROG1} },
{ KE_KEY, 0x12, {KEY_PROG2} },
- { KE_WIFI, 0x30 },
+ { KE_WIFI, 0x30 },
{ KE_KEY, 0x22, {KEY_REWIND} },
{ KE_KEY, 0x23, {KEY_FORWARD} },
{ KE_KEY, 0x24, {KEY_PLAYPAUSE} },
@@ -985,6 +999,8 @@ static int __init select_keymap(void)
if (keymap_name != NULL) {
if (strcmp (keymap_name, "1557/MS2141") == 0)
keymap = keymap_wistron_ms2141;
+ else if (strcmp (keymap_name, "aopen1557") == 0)
+ keymap = keymap_aopen_1557;
else if (strcmp (keymap_name, "prestigio") == 0)
keymap = keymap_prestigio;
else if (strcmp (keymap_name, "generic") == 0)
@@ -1009,8 +1025,8 @@ static int __init select_keymap(void)
static struct input_polled_dev *wistron_idev;
static unsigned long jiffies_last_press;
-static int wifi_enabled;
-static int bluetooth_enabled;
+static bool wifi_enabled;
+static bool bluetooth_enabled;
static void report_key(struct input_dev *dev, unsigned int keycode)
{
@@ -1053,24 +1069,24 @@ static struct led_classdev wistron_wifi_led = {
static void __devinit wistron_led_init(struct device *parent)
{
- if (have_leds & FE_WIFI_LED) {
+ if (leds_present & FE_WIFI_LED) {
u16 wifi = bios_get_default_setting(WIFI);
if (wifi & 1) {
wistron_wifi_led.brightness = (wifi & 2) ? LED_FULL : LED_OFF;
if (led_classdev_register(parent, &wistron_wifi_led))
- have_leds &= ~FE_WIFI_LED;
+ leds_present &= ~FE_WIFI_LED;
else
bios_set_state(WIFI, wistron_wifi_led.brightness);
} else
- have_leds &= ~FE_WIFI_LED;
+ leds_present &= ~FE_WIFI_LED;
}
- if (have_leds & FE_MAIL_LED) {
+ if (leds_present & FE_MAIL_LED) {
/* bios_get_default_setting(MAIL) always retuns 0, so just turn the led off */
wistron_mail_led.brightness = LED_OFF;
if (led_classdev_register(parent, &wistron_mail_led))
- have_leds &= ~FE_MAIL_LED;
+ leds_present &= ~FE_MAIL_LED;
else
bios_set_state(MAIL_LED, wistron_mail_led.brightness);
}
@@ -1078,28 +1094,28 @@ static void __devinit wistron_led_init(struct device *parent)
static void __devexit wistron_led_remove(void)
{
- if (have_leds & FE_MAIL_LED)
+ if (leds_present & FE_MAIL_LED)
led_classdev_unregister(&wistron_mail_led);
- if (have_leds & FE_WIFI_LED)
+ if (leds_present & FE_WIFI_LED)
led_classdev_unregister(&wistron_wifi_led);
}
static inline void wistron_led_suspend(void)
{
- if (have_leds & FE_MAIL_LED)
+ if (leds_present & FE_MAIL_LED)
led_classdev_suspend(&wistron_mail_led);
- if (have_leds & FE_WIFI_LED)
+ if (leds_present & FE_WIFI_LED)
led_classdev_suspend(&wistron_wifi_led);
}
static inline void wistron_led_resume(void)
{
- if (have_leds & FE_MAIL_LED)
+ if (leds_present & FE_MAIL_LED)
led_classdev_resume(&wistron_mail_led);
- if (have_leds & FE_WIFI_LED)
+ if (leds_present & FE_WIFI_LED)
led_classdev_resume(&wistron_wifi_led);
}
@@ -1312,7 +1328,7 @@ static int __devinit wistron_probe(struct platform_device *dev)
if (have_wifi) {
u16 wifi = bios_get_default_setting(WIFI);
if (wifi & 1)
- wifi_enabled = (wifi & 2) ? 1 : 0;
+ wifi_enabled = wifi & 2;
else
have_wifi = 0;
@@ -1323,15 +1339,16 @@ static int __devinit wistron_probe(struct platform_device *dev)
if (have_bluetooth) {
u16 bt = bios_get_default_setting(BLUETOOTH);
if (bt & 1)
- bluetooth_enabled = (bt & 2) ? 1 : 0;
+ bluetooth_enabled = bt & 2;
else
- have_bluetooth = 0;
+ have_bluetooth = false;
if (have_bluetooth)
bios_set_state(BLUETOOTH, bluetooth_enabled);
}
wistron_led_init(&dev->dev);
+
err = setup_input_dev();
if (err) {
bios_detach();
@@ -1352,7 +1369,7 @@ static int __devexit wistron_remove(struct platform_device *dev)
}
#ifdef CONFIG_PM
-static int wistron_suspend(struct platform_device *dev, pm_message_t state)
+static int wistron_suspend(struct device *dev)
{
if (have_wifi)
bios_set_state(WIFI, 0);
@@ -1361,10 +1378,11 @@ static int wistron_suspend(struct platform_device *dev, pm_message_t state)
bios_set_state(BLUETOOTH, 0);
wistron_led_suspend();
+
return 0;
}
-static int wistron_resume(struct platform_device *dev)
+static int wistron_resume(struct device *dev)
{
if (have_wifi)
bios_set_state(WIFI, wifi_enabled);
@@ -1373,24 +1391,30 @@ static int wistron_resume(struct platform_device *dev)
bios_set_state(BLUETOOTH, bluetooth_enabled);
wistron_led_resume();
+
poll_bios(true);
return 0;
}
-#else
-#define wistron_suspend NULL
-#define wistron_resume NULL
+
+static const struct dev_pm_ops wistron_pm_ops = {
+ .suspend = wistron_suspend,
+ .resume = wistron_resume,
+ .poweroff = wistron_suspend,
+ .restore = wistron_resume,
+};
#endif
static struct platform_driver wistron_driver = {
.driver = {
.name = "wistron-bios",
.owner = THIS_MODULE,
+#if CONFIG_PM
+ .pm = &wistron_pm_ops,
+#endif
},
.probe = wistron_probe,
.remove = __devexit_p(wistron_remove),
- .suspend = wistron_suspend,
- .resume = wistron_resume,
};
static int __init wb_module_init(void)
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 8a2c5b14c8d..3feeb3af8ab 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -107,6 +107,14 @@ config MOUSE_PS2_ELANTECH
entries. For further information,
see <file:Documentation/input/elantech.txt>.
+config MOUSE_PS2_SENTELIC
+ bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
+ depends on MOUSE_PS2
+ help
+ Say Y here if you have a laptop (such as MSI WIND Netbook)
+ with Sentelic Finger Sensing Pad touchpad.
+
+ If unsure, say N.
config MOUSE_PS2_TOUCHKIT
bool "eGalax TouchKit PS/2 protocol extension"
@@ -262,14 +270,6 @@ config MOUSE_VSXXXAA
described in the source file). This driver also works with the
digitizer (VSXXX-AB) DEC produced.
-config MOUSE_HIL
- tristate "HIL pointers (mice etc)."
- depends on GSC || HP300
- select HP_SDC
- select HIL_MLC
- help
- Say Y here to support HIL pointers.
-
config MOUSE_GPIO
tristate "GPIO mouse"
depends on GENERIC_GPIO
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 010f265ec15..570c84a4a65 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -9,7 +9,6 @@ obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
-obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o
obj-$(CONFIG_MOUSE_INPORT) += inport.o
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
@@ -28,5 +27,6 @@ psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o
psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o
psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o
psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o
+psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o
psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o
psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 5547e2429fb..f36110689aa 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -279,7 +279,7 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int
* subsequent commands. It looks like glidepad is behind stickpointer,
* I'd thought it would be other way around...
*/
-static int alps_passthrough_mode(struct psmouse *psmouse, int enable)
+static int alps_passthrough_mode(struct psmouse *psmouse, bool enable)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
int cmd = enable ? PSMOUSE_CMD_SETSCALE21 : PSMOUSE_CMD_SETSCALE11;
@@ -367,16 +367,16 @@ static int alps_poll(struct psmouse *psmouse)
{
struct alps_data *priv = psmouse->private;
unsigned char buf[6];
- int poll_failed;
+ bool poll_failed;
if (priv->i->flags & ALPS_PASS)
- alps_passthrough_mode(psmouse, 1);
+ alps_passthrough_mode(psmouse, true);
poll_failed = ps2_command(&psmouse->ps2dev, buf,
PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0;
if (priv->i->flags & ALPS_PASS)
- alps_passthrough_mode(psmouse, 0);
+ alps_passthrough_mode(psmouse, false);
if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0)
return -1;
@@ -401,10 +401,12 @@ static int alps_hw_init(struct psmouse *psmouse, int *version)
if (!priv->i)
return -1;
- if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 1))
+ if ((priv->i->flags & ALPS_PASS) &&
+ alps_passthrough_mode(psmouse, true)) {
return -1;
+ }
- if (alps_tap_mode(psmouse, 1)) {
+ if (alps_tap_mode(psmouse, true)) {
printk(KERN_WARNING "alps.c: Failed to enable hardware tapping\n");
return -1;
}
@@ -414,8 +416,10 @@ static int alps_hw_init(struct psmouse *psmouse, int *version)
return -1;
}
- if ((priv->i->flags & ALPS_PASS) && alps_passthrough_mode(psmouse, 0))
+ if ((priv->i->flags & ALPS_PASS) &&
+ alps_passthrough_mode(psmouse, false)) {
return -1;
+ }
/* ALPS needs stream mode, otherwise it won't report any data */
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSTREAM)) {
@@ -519,7 +523,7 @@ init_fail:
return -1;
}
-int alps_detect(struct psmouse *psmouse, int set_properties)
+int alps_detect(struct psmouse *psmouse, bool set_properties)
{
int version;
const struct alps_model_info *model;
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index 4bbddc99962..bc87936fee1 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -26,10 +26,10 @@ struct alps_data {
};
#ifdef CONFIG_MOUSE_PS2_ALPS
-int alps_detect(struct psmouse *psmouse, int set_properties);
+int alps_detect(struct psmouse *psmouse, bool set_properties);
int alps_init(struct psmouse *psmouse);
#else
-inline int alps_detect(struct psmouse *psmouse, int set_properties)
+inline int alps_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
index 2d8fc0bf692..0d1d33468b4 100644
--- a/drivers/input/mouse/bcm5974.c
+++ b/drivers/input/mouse/bcm5974.c
@@ -317,7 +317,7 @@ static int report_tp_state(struct bcm5974 *dev, int size)
const struct tp_finger *f;
struct input_dev *input = dev->input;
int raw_p, raw_w, raw_x, raw_y, raw_n;
- int ptest = 0, origin = 0, ibt = 0, nmin = 0, nmax = 0;
+ int ptest, origin, ibt = 0, nmin = 0, nmax = 0;
int abs_p = 0, abs_w = 0, abs_x = 0, abs_y = 0;
if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0)
@@ -345,21 +345,22 @@ static int report_tp_state(struct bcm5974 *dev, int size)
/* set the integrated button if applicable */
if (c->tp_type == TYPE2)
ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
- }
- /* while tracking finger still valid, count all fingers */
- if (ptest > PRESSURE_LOW && origin) {
- abs_p = ptest;
- abs_w = int2bound(&c->w, raw_w);
- abs_x = int2bound(&c->x, raw_x - c->x.devmin);
- abs_y = int2bound(&c->y, c->y.devmax - raw_y);
- while (raw_n--) {
- ptest = int2bound(&c->p, raw2int(f->force_major));
- if (ptest > PRESSURE_LOW)
- nmax++;
- if (ptest > PRESSURE_HIGH)
- nmin++;
- f++;
+ /* while tracking finger still valid, count all fingers */
+ if (ptest > PRESSURE_LOW && origin) {
+ abs_p = ptest;
+ abs_w = int2bound(&c->w, raw_w);
+ abs_x = int2bound(&c->x, raw_x - c->x.devmin);
+ abs_y = int2bound(&c->y, c->y.devmax - raw_y);
+ while (raw_n--) {
+ ptest = int2bound(&c->p,
+ raw2int(f->force_major));
+ if (ptest > PRESSURE_LOW)
+ nmax++;
+ if (ptest > PRESSURE_HIGH)
+ nmin++;
+ f++;
+ }
}
}
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 4bc78892ba9..fda35e615ab 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -553,7 +553,7 @@ static struct attribute_group elantech_attr_group = {
/*
* Use magic knock to detect Elantech touchpad
*/
-int elantech_detect(struct psmouse *psmouse, int set_properties)
+int elantech_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[3];
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index ed848cc8081..feac5f7af96 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -109,10 +109,10 @@ struct elantech_data {
};
#ifdef CONFIG_MOUSE_PS2_ELANTECH
-int elantech_detect(struct psmouse *psmouse, int set_properties);
+int elantech_detect(struct psmouse *psmouse, bool set_properties);
int elantech_init(struct psmouse *psmouse);
#else
-static inline int elantech_detect(struct psmouse *psmouse, int set_properties)
+static inline int elantech_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c
index a1ad2f1a7bb..de1e553028b 100644
--- a/drivers/input/mouse/hgpk.c
+++ b/drivers/input/mouse/hgpk.c
@@ -367,7 +367,36 @@ static ssize_t hgpk_set_powered(struct psmouse *psmouse, void *data,
}
__PSMOUSE_DEFINE_ATTR(powered, S_IWUSR | S_IRUGO, NULL,
- hgpk_show_powered, hgpk_set_powered, 0);
+ hgpk_show_powered, hgpk_set_powered, false);
+
+static ssize_t hgpk_trigger_recal_show(struct psmouse *psmouse,
+ void *data, char *buf)
+{
+ return -EINVAL;
+}
+
+static ssize_t hgpk_trigger_recal(struct psmouse *psmouse, void *data,
+ const char *buf, size_t count)
+{
+ struct hgpk_data *priv = psmouse->private;
+ unsigned long value;
+ int err;
+
+ err = strict_strtoul(buf, 10, &value);
+ if (err || value != 1)
+ return -EINVAL;
+
+ /*
+ * We queue work instead of doing recalibration right here
+ * to avoid adding locking to to hgpk_force_recalibrate()
+ * since workqueue provides serialization.
+ */
+ psmouse_queue_work(psmouse, &priv->recalib_wq, 0);
+ return count;
+}
+
+__PSMOUSE_DEFINE_ATTR(recalibrate, S_IWUSR | S_IRUGO, NULL,
+ hgpk_trigger_recal_show, hgpk_trigger_recal, false);
static void hgpk_disconnect(struct psmouse *psmouse)
{
@@ -375,6 +404,11 @@ static void hgpk_disconnect(struct psmouse *psmouse)
device_remove_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_powered.dattr);
+
+ if (psmouse->model >= HGPK_MODEL_C)
+ device_remove_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_recalibrate.dattr);
+
psmouse_reset(psmouse);
kfree(priv);
}
@@ -423,10 +457,25 @@ static int hgpk_register(struct psmouse *psmouse)
err = device_create_file(&psmouse->ps2dev.serio->dev,
&psmouse_attr_powered.dattr);
- if (err)
- hgpk_err(psmouse, "Failed to create sysfs attribute\n");
+ if (err) {
+ hgpk_err(psmouse, "Failed creating 'powered' sysfs node\n");
+ return err;
+ }
- return err;
+ /* C-series touchpads added the recalibrate command */
+ if (psmouse->model >= HGPK_MODEL_C) {
+ err = device_create_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_recalibrate.dattr);
+ if (err) {
+ hgpk_err(psmouse,
+ "Failed creating 'recalibrate' sysfs node\n");
+ device_remove_file(&psmouse->ps2dev.serio->dev,
+ &psmouse_attr_powered.dattr);
+ return err;
+ }
+ }
+
+ return 0;
}
int hgpk_init(struct psmouse *psmouse)
@@ -440,7 +489,7 @@ int hgpk_init(struct psmouse *psmouse)
psmouse->private = priv;
priv->psmouse = psmouse;
- priv->powered = 1;
+ priv->powered = true;
INIT_DELAYED_WORK(&priv->recalib_wq, hgpk_recalib_work);
err = psmouse_reset(psmouse);
@@ -483,7 +532,7 @@ static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse)
return param[2];
}
-int hgpk_detect(struct psmouse *psmouse, int set_properties)
+int hgpk_detect(struct psmouse *psmouse, bool set_properties)
{
int version;
diff --git a/drivers/input/mouse/hgpk.h b/drivers/input/mouse/hgpk.h
index a4b2a96f5f5..d61cfd3ee9c 100644
--- a/drivers/input/mouse/hgpk.h
+++ b/drivers/input/mouse/hgpk.h
@@ -15,7 +15,7 @@ enum hgpk_model_t {
struct hgpk_data {
struct psmouse *psmouse;
- int powered;
+ bool powered;
int count, x_tally, y_tally; /* hardware workaround stuff */
unsigned long recalib_window;
struct delayed_work recalib_wq;
@@ -33,10 +33,10 @@ struct hgpk_data {
dev_notice(&(psmouse)->ps2dev.serio->dev, format, ## arg)
#ifdef CONFIG_MOUSE_PS2_OLPC
-int hgpk_detect(struct psmouse *psmouse, int set_properties);
+int hgpk_detect(struct psmouse *psmouse, bool set_properties);
int hgpk_init(struct psmouse *psmouse);
#else
-static inline int hgpk_detect(struct psmouse *psmouse, int set_properties)
+static inline int hgpk_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENODEV;
}
diff --git a/drivers/input/mouse/hil_ptr.c b/drivers/input/mouse/hil_ptr.c
deleted file mode 100644
index 3263ce083bf..00000000000
--- a/drivers/input/mouse/hil_ptr.c
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
- * Generic linux-input device driver for axis-bearing devices
- *
- * Copyright (c) 2001 Brian S. Julin
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions, and the following disclaimer,
- * without modification.
- * 2. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * Alternatively, this software may be distributed under the terms of the
- * GNU General Public License ("GPL").
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- *
- * References:
- * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A
- *
- */
-
-#include <linux/hil.h>
-#include <linux/input.h>
-#include <linux/serio.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/pci_ids.h>
-
-#define PREFIX "HIL PTR: "
-#define HIL_GENERIC_NAME "HIL pointer device"
-
-MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
-MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_ALIAS("serio:ty03pr25id0Fex*");
-
-#define TABLET_SIMULATES_MOUSE /* allow tablet to be used as mouse */
-#undef TABLET_AUTOADJUST /* auto-adjust valid tablet ranges */
-
-
-#define HIL_PTR_MAX_LENGTH 16
-
-struct hil_ptr {
- struct input_dev *dev;
- struct serio *serio;
-
- /* Input buffer and index for packets from HIL bus. */
- hil_packet data[HIL_PTR_MAX_LENGTH];
- int idx4; /* four counts per packet */
-
- /* Raw device info records from HIL bus, see hil.h for fields. */
- char idd[HIL_PTR_MAX_LENGTH]; /* DID byte and IDD record */
- char rsc[HIL_PTR_MAX_LENGTH]; /* RSC record */
- char exd[HIL_PTR_MAX_LENGTH]; /* EXD record */
- char rnm[HIL_PTR_MAX_LENGTH + 1]; /* RNM record + NULL term. */
-
- /* Extra device details not contained in struct input_dev. */
- unsigned int nbtn, naxes;
- unsigned int btnmap[7];
-
- /* Something to sleep around with. */
- struct semaphore sem;
-};
-
-/* Process a complete packet after transfer from the HIL */
-static void hil_ptr_process_record(struct hil_ptr *ptr)
-{
- struct input_dev *dev = ptr->dev;
- hil_packet *data = ptr->data;
- hil_packet p;
- int idx, i, cnt, laxis;
- int ax16, absdev;
-
- idx = ptr->idx4/4;
- p = data[idx - 1];
-
- if ((p & ~HIL_CMDCT_POL) ==
- (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
- goto report;
- if ((p & ~HIL_CMDCT_RPL) ==
- (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL))
- goto report;
-
- /* Not a poll response. See if we are loading config records. */
- switch (p & HIL_PKT_DATA_MASK) {
- case HIL_CMD_IDD:
- for (i = 0; i < idx; i++)
- ptr->idd[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
- for (; i < HIL_PTR_MAX_LENGTH; i++)
- ptr->idd[i] = 0;
- break;
-
- case HIL_CMD_RSC:
- for (i = 0; i < idx; i++)
- ptr->rsc[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
- for (; i < HIL_PTR_MAX_LENGTH; i++)
- ptr->rsc[i] = 0;
- break;
-
- case HIL_CMD_EXD:
- for (i = 0; i < idx; i++)
- ptr->exd[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
- for (; i < HIL_PTR_MAX_LENGTH; i++)
- ptr->exd[i] = 0;
- break;
-
- case HIL_CMD_RNM:
- for (i = 0; i < idx; i++)
- ptr->rnm[i] = ptr->data[i] & HIL_PKT_DATA_MASK;
- for (; i < HIL_PTR_MAX_LENGTH + 1; i++)
- ptr->rnm[i] = 0;
- break;
-
- default:
- /* These occur when device isn't present */
- if (p == (HIL_ERR_INT | HIL_PKT_CMD))
- break;
- /* Anything else we'd like to know about. */
- printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
- break;
- }
- goto out;
-
- report:
- if ((p & HIL_CMDCT_POL) != idx - 1) {
- printk(KERN_WARNING PREFIX
- "Malformed poll packet %x (idx = %i)\n", p, idx);
- goto out;
- }
-
- i = (ptr->data[0] & HIL_POL_AXIS_ALT) ? 3 : 0;
- laxis = ptr->data[0] & HIL_POL_NUM_AXES_MASK;
- laxis += i;
-
- ax16 = ptr->idd[1] & HIL_IDD_HEADER_16BIT; /* 8 or 16bit resolution */
- absdev = ptr->idd[1] & HIL_IDD_HEADER_ABS;
-
- for (cnt = 1; i < laxis; i++) {
- unsigned int lo,hi,val;
- lo = ptr->data[cnt++] & HIL_PKT_DATA_MASK;
- hi = ax16 ? (ptr->data[cnt++] & HIL_PKT_DATA_MASK) : 0;
- if (absdev) {
- val = lo + (hi<<8);
-#ifdef TABLET_AUTOADJUST
- if (val < dev->absmin[ABS_X + i])
- dev->absmin[ABS_X + i] = val;
- if (val > dev->absmax[ABS_X + i])
- dev->absmax[ABS_X + i] = val;
-#endif
- if (i%3) val = dev->absmax[ABS_X + i] - val;
- input_report_abs(dev, ABS_X + i, val);
- } else {
- val = (int) (((int8_t)lo) | ((int8_t)hi<<8));
- if (i%3)
- val *= -1;
- input_report_rel(dev, REL_X + i, val);
- }
- }
-
- while (cnt < idx - 1) {
- unsigned int btn;
- int up;
- btn = ptr->data[cnt++];
- up = btn & 1;
- btn &= 0xfe;
- if (btn == 0x8e)
- continue; /* TODO: proximity == touch? */
- else
- if ((btn > 0x8c) || (btn < 0x80))
- continue;
- btn = (btn - 0x80) >> 1;
- btn = ptr->btnmap[btn];
- input_report_key(dev, btn, !up);
- }
- input_sync(dev);
- out:
- ptr->idx4 = 0;
- up(&ptr->sem);
-}
-
-static void hil_ptr_process_err(struct hil_ptr *ptr)
-{
- printk(KERN_WARNING PREFIX "errored HIL packet\n");
- ptr->idx4 = 0;
- up(&ptr->sem);
-}
-
-static irqreturn_t hil_ptr_interrupt(struct serio *serio,
- unsigned char data, unsigned int flags)
-{
- struct hil_ptr *ptr;
- hil_packet packet;
- int idx;
-
- ptr = serio_get_drvdata(serio);
- BUG_ON(ptr == NULL);
-
- if (ptr->idx4 >= (HIL_PTR_MAX_LENGTH * sizeof(hil_packet))) {
- hil_ptr_process_err(ptr);
- return IRQ_HANDLED;
- }
- idx = ptr->idx4/4;
- if (!(ptr->idx4 % 4))
- ptr->data[idx] = 0;
- packet = ptr->data[idx];
- packet |= ((hil_packet)data) << ((3 - (ptr->idx4 % 4)) * 8);
- ptr->data[idx] = packet;
-
- /* Records of N 4-byte hil_packets must terminate with a command. */
- if ((++(ptr->idx4)) % 4)
- return IRQ_HANDLED;
- if ((packet & 0xffff0000) != HIL_ERR_INT) {
- hil_ptr_process_err(ptr);
- return IRQ_HANDLED;
- }
- if (packet & HIL_PKT_CMD)
- hil_ptr_process_record(ptr);
-
- return IRQ_HANDLED;
-}
-
-static void hil_ptr_disconnect(struct serio *serio)
-{
- struct hil_ptr *ptr;
-
- ptr = serio_get_drvdata(serio);
- BUG_ON(ptr == NULL);
-
- serio_close(serio);
- input_unregister_device(ptr->dev);
- kfree(ptr);
-}
-
-static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
-{
- struct hil_ptr *ptr;
- const char *txt;
- unsigned int i, naxsets, btntype;
- uint8_t did, *idd;
- int error;
-
- ptr = kzalloc(sizeof(struct hil_ptr), GFP_KERNEL);
- if (!ptr)
- return -ENOMEM;
-
- ptr->dev = input_allocate_device();
- if (!ptr->dev) {
- error = -ENOMEM;
- goto bail0;
- }
-
- error = serio_open(serio, driver);
- if (error)
- goto bail1;
-
- serio_set_drvdata(serio, ptr);
- ptr->serio = serio;
-
- init_MUTEX_LOCKED(&ptr->sem);
-
- /* Get device info. MLC driver supplies devid/status/etc. */
- serio->write(serio, 0);
- serio->write(serio, 0);
- serio->write(serio, HIL_PKT_CMD >> 8);
- serio->write(serio, HIL_CMD_IDD);
- down(&ptr->sem);
-
- serio->write(serio, 0);
- serio->write(serio, 0);
- serio->write(serio, HIL_PKT_CMD >> 8);
- serio->write(serio, HIL_CMD_RSC);
- down(&ptr->sem);
-
- serio->write(serio, 0);
- serio->write(serio, 0);
- serio->write(serio, HIL_PKT_CMD >> 8);
- serio->write(serio, HIL_CMD_RNM);
- down(&ptr->sem);
-
- serio->write(serio, 0);
- serio->write(serio, 0);
- serio->write(serio, HIL_PKT_CMD >> 8);
- serio->write(serio, HIL_CMD_EXD);
- down(&ptr->sem);
-
- up(&ptr->sem);
-
- did = ptr->idd[0];
- idd = ptr->idd + 1;
- txt = "unknown";
-
- if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
- ptr->dev->evbit[0] = BIT_MASK(EV_REL);
- txt = "relative";
- }
-
- if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_ABS) {
- ptr->dev->evbit[0] = BIT_MASK(EV_ABS);
- txt = "absolute";
- }
-
- if (!ptr->dev->evbit[0]) {
- error = -ENODEV;
- goto bail2;
- }
-
- ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd);
- if (ptr->nbtn)
- ptr->dev->evbit[0] |= BIT_MASK(EV_KEY);
-
- naxsets = HIL_IDD_NUM_AXSETS(*idd);
- ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd);
-
- printk(KERN_INFO PREFIX "HIL pointer device found (did: 0x%02x, axis: %s)\n",
- did, txt);
- printk(KERN_INFO PREFIX "HIL pointer has %i buttons and %i sets of %i axes\n",
- ptr->nbtn, naxsets, ptr->naxes);
-
- btntype = BTN_MISC;
- if ((did & HIL_IDD_DID_ABS_TABLET_MASK) == HIL_IDD_DID_ABS_TABLET)
-#ifdef TABLET_SIMULATES_MOUSE
- btntype = BTN_TOUCH;
-#else
- btntype = BTN_DIGI;
-#endif
- if ((did & HIL_IDD_DID_ABS_TSCREEN_MASK) == HIL_IDD_DID_ABS_TSCREEN)
- btntype = BTN_TOUCH;
-
- if ((did & HIL_IDD_DID_REL_MOUSE_MASK) == HIL_IDD_DID_REL_MOUSE)
- btntype = BTN_MOUSE;
-
- for (i = 0; i < ptr->nbtn; i++) {
- set_bit(btntype | i, ptr->dev->keybit);
- ptr->btnmap[i] = btntype | i;
- }
-
- if (btntype == BTN_MOUSE) {
- /* Swap buttons 2 and 3 */
- ptr->btnmap[1] = BTN_MIDDLE;
- ptr->btnmap[2] = BTN_RIGHT;
- }
-
- if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
- for (i = 0; i < ptr->naxes; i++)
- set_bit(REL_X + i, ptr->dev->relbit);
- for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++)
- set_bit(REL_X + i, ptr->dev->relbit);
- } else {
- for (i = 0; i < ptr->naxes; i++) {
- set_bit(ABS_X + i, ptr->dev->absbit);
- ptr->dev->absmin[ABS_X + i] = 0;
- ptr->dev->absmax[ABS_X + i] =
- HIL_IDD_AXIS_MAX((ptr->idd + 1), i);
- }
- for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) {
- set_bit(ABS_X + i, ptr->dev->absbit);
- ptr->dev->absmin[ABS_X + i] = 0;
- ptr->dev->absmax[ABS_X + i] =
- HIL_IDD_AXIS_MAX((ptr->idd + 1), (i - 3));
- }
-#ifdef TABLET_AUTOADJUST
- for (i = 0; i < ABS_MAX; i++) {
- int diff = ptr->dev->absmax[ABS_X + i] / 10;
- ptr->dev->absmin[ABS_X + i] += diff;
- ptr->dev->absmax[ABS_X + i] -= diff;
- }
-#endif
- }
-
- ptr->dev->name = strlen(ptr->rnm) ? ptr->rnm : HIL_GENERIC_NAME;
-
- ptr->dev->id.bustype = BUS_HIL;
- ptr->dev->id.vendor = PCI_VENDOR_ID_HP;
- ptr->dev->id.product = 0x0001; /* TODO: get from ptr->rsc */
- ptr->dev->id.version = 0x0100; /* TODO: get from ptr->rsc */
- ptr->dev->dev.parent = &serio->dev;
-
- error = input_register_device(ptr->dev);
- if (error) {
- printk(KERN_INFO PREFIX "Unable to register input device\n");
- goto bail2;
- }
-
- printk(KERN_INFO "input: %s (%s), ID: %d\n",
- ptr->dev->name,
- (btntype == BTN_MOUSE) ? "HIL mouse":"HIL tablet or touchpad",
- did);
-
- return 0;
-
- bail2:
- serio_close(serio);
- bail1:
- input_free_device(ptr->dev);
- bail0:
- kfree(ptr);
- serio_set_drvdata(serio, NULL);
- return error;
-}
-
-static struct serio_device_id hil_ptr_ids[] = {
- {
- .type = SERIO_HIL_MLC,
- .proto = SERIO_HIL,
- .id = SERIO_ANY,
- .extra = SERIO_ANY,
- },
- { 0 }
-};
-
-static struct serio_driver hil_ptr_serio_driver = {
- .driver = {
- .name = "hil_ptr",
- },
- .description = "HP HIL mouse/tablet driver",
- .id_table = hil_ptr_ids,
- .connect = hil_ptr_connect,
- .disconnect = hil_ptr_disconnect,
- .interrupt = hil_ptr_interrupt
-};
-
-static int __init hil_ptr_init(void)
-{
- return serio_register_driver(&hil_ptr_serio_driver);
-}
-
-static void __exit hil_ptr_exit(void)
-{
- serio_unregister_driver(&hil_ptr_serio_driver);
-}
-
-module_init(hil_ptr_init);
-module_exit(hil_ptr_exit);
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c
index dcd4236af1e..5e630869440 100644
--- a/drivers/input/mouse/lifebook.c
+++ b/drivers/input/mouse/lifebook.c
@@ -33,11 +33,11 @@ static int lifebook_set_serio_phys(const struct dmi_system_id *d)
return 0;
}
-static unsigned char lifebook_use_6byte_proto;
+static bool lifebook_use_6byte_proto;
static int lifebook_set_6byte_proto(const struct dmi_system_id *d)
{
- lifebook_use_6byte_proto = 1;
+ lifebook_use_6byte_proto = true;
return 0;
}
@@ -125,7 +125,7 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
struct input_dev *dev1 = psmouse->dev;
struct input_dev *dev2 = priv ? priv->dev2 : NULL;
unsigned char *packet = psmouse->packet;
- int relative_packet = packet[0] & 0x08;
+ bool relative_packet = packet[0] & 0x08;
if (relative_packet || !lifebook_use_6byte_proto) {
if (psmouse->pktcnt != 3)
@@ -242,7 +242,7 @@ static void lifebook_disconnect(struct psmouse *psmouse)
psmouse->private = NULL;
}
-int lifebook_detect(struct psmouse *psmouse, int set_properties)
+int lifebook_detect(struct psmouse *psmouse, bool set_properties)
{
if (!dmi_check_system(lifebook_dmi_table))
return -1;
diff --git a/drivers/input/mouse/lifebook.h b/drivers/input/mouse/lifebook.h
index c1647cf036c..407cb226bc0 100644
--- a/drivers/input/mouse/lifebook.h
+++ b/drivers/input/mouse/lifebook.h
@@ -12,10 +12,10 @@
#define _LIFEBOOK_H
#ifdef CONFIG_MOUSE_PS2_LIFEBOOK
-int lifebook_detect(struct psmouse *psmouse, int set_properties);
+int lifebook_detect(struct psmouse *psmouse, bool set_properties);
int lifebook_init(struct psmouse *psmouse);
#else
-inline int lifebook_detect(struct psmouse *psmouse, int set_properties)
+inline int lifebook_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
index 390f1dbb98a..de745d75116 100644
--- a/drivers/input/mouse/logips2pp.c
+++ b/drivers/input/mouse/logips2pp.c
@@ -130,14 +130,11 @@ static int ps2pp_cmd(struct psmouse *psmouse, unsigned char *param, unsigned cha
* 0 - disabled
*/
-static void ps2pp_set_smartscroll(struct psmouse *psmouse, unsigned int smartscroll)
+static void ps2pp_set_smartscroll(struct psmouse *psmouse, bool smartscroll)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
- if (smartscroll > 1)
- smartscroll = 1;
-
ps2pp_cmd(psmouse, param, 0x32);
param[0] = 0;
@@ -149,12 +146,14 @@ static void ps2pp_set_smartscroll(struct psmouse *psmouse, unsigned int smartscr
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
}
-static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse, void *data, char *buf)
+static ssize_t ps2pp_attr_show_smartscroll(struct psmouse *psmouse,
+ void *data, char *buf)
{
- return sprintf(buf, "%d\n", psmouse->smartscroll ? 1 : 0);
+ return sprintf(buf, "%d\n", psmouse->smartscroll);
}
-static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data, const char *buf, size_t count)
+static ssize_t ps2pp_attr_set_smartscroll(struct psmouse *psmouse, void *data,
+ const char *buf, size_t count)
{
unsigned long value;
@@ -261,29 +260,29 @@ static const struct ps2pp_info *get_model_info(unsigned char model)
static void ps2pp_set_model_properties(struct psmouse *psmouse,
const struct ps2pp_info *model_info,
- int using_ps2pp)
+ bool using_ps2pp)
{
struct input_dev *input_dev = psmouse->dev;
if (model_info->features & PS2PP_SIDE_BTN)
- set_bit(BTN_SIDE, input_dev->keybit);
+ __set_bit(BTN_SIDE, input_dev->keybit);
if (model_info->features & PS2PP_EXTRA_BTN)
- set_bit(BTN_EXTRA, input_dev->keybit);
+ __set_bit(BTN_EXTRA, input_dev->keybit);
if (model_info->features & PS2PP_TASK_BTN)
- set_bit(BTN_TASK, input_dev->keybit);
+ __set_bit(BTN_TASK, input_dev->keybit);
if (model_info->features & PS2PP_NAV_BTN) {
- set_bit(BTN_FORWARD, input_dev->keybit);
- set_bit(BTN_BACK, input_dev->keybit);
+ __set_bit(BTN_FORWARD, input_dev->keybit);
+ __set_bit(BTN_BACK, input_dev->keybit);
}
if (model_info->features & PS2PP_WHEEL)
- set_bit(REL_WHEEL, input_dev->relbit);
+ __set_bit(REL_WHEEL, input_dev->relbit);
if (model_info->features & PS2PP_HWHEEL)
- set_bit(REL_HWHEEL, input_dev->relbit);
+ __set_bit(REL_HWHEEL, input_dev->relbit);
switch (model_info->kind) {
case PS2PP_KIND_WHEEL:
@@ -321,13 +320,13 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
* that support it.
*/
-int ps2pp_init(struct psmouse *psmouse, int set_properties)
+int ps2pp_init(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
unsigned char model, buttons;
const struct ps2pp_info *model_info;
- int use_ps2pp = 0;
+ bool use_ps2pp = false;
int error;
param[0] = 0;
@@ -364,7 +363,7 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties)
param[0] = 0;
if (!ps2_command(ps2dev, param, 0x13d1) &&
param[0] == 0x06 && param[1] == 0x00 && param[2] == 0x14) {
- use_ps2pp = 1;
+ use_ps2pp = true;
}
} else {
@@ -376,8 +375,8 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties)
if ((param[0] & 0x78) == 0x48 &&
(param[1] & 0xf3) == 0xc2 &&
(param[2] & 0x03) == ((param[1] >> 2) & 3)) {
- ps2pp_set_smartscroll(psmouse, psmouse->smartscroll);
- use_ps2pp = 1;
+ ps2pp_set_smartscroll(psmouse, false);
+ use_ps2pp = true;
}
}
}
@@ -406,7 +405,7 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties)
}
if (buttons < 3)
- clear_bit(BTN_MIDDLE, psmouse->dev->keybit);
+ __clear_bit(BTN_MIDDLE, psmouse->dev->keybit);
if (model_info)
ps2pp_set_model_properties(psmouse, model_info, use_ps2pp);
diff --git a/drivers/input/mouse/logips2pp.h b/drivers/input/mouse/logips2pp.h
index 6e5712525fd..0c186f0282d 100644
--- a/drivers/input/mouse/logips2pp.h
+++ b/drivers/input/mouse/logips2pp.h
@@ -12,9 +12,9 @@
#define _LOGIPS2PP_H
#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
-int ps2pp_init(struct psmouse *psmouse, int set_properties);
+int ps2pp_init(struct psmouse *psmouse, bool set_properties);
#else
-inline int ps2pp_init(struct psmouse *psmouse, int set_properties)
+inline int ps2pp_init(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index b407b355dce..690aed90543 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -30,6 +30,7 @@
#include "trackpoint.h"
#include "touchkit_ps2.h"
#include "elantech.h"
+#include "sentelic.h"
#define DRIVER_DESC "PS/2 mouse driver"
@@ -108,10 +109,10 @@ static struct workqueue_struct *kpsmoused_wq;
struct psmouse_protocol {
enum psmouse_type type;
+ bool maxproto;
const char *name;
const char *alias;
- int maxproto;
- int (*detect)(struct psmouse *, int);
+ int (*detect)(struct psmouse *, bool);
int (*init)(struct psmouse *);
};
@@ -216,7 +217,7 @@ void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
{
psmouse->state = new_state;
- psmouse->pktcnt = psmouse->out_of_sync = 0;
+ psmouse->pktcnt = psmouse->out_of_sync_cnt = 0;
psmouse->ps2dev.flags = 0;
psmouse->last = jiffies;
}
@@ -249,7 +250,7 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
if (psmouse->state == PSMOUSE_ACTIVATED) {
printk(KERN_WARNING "psmouse.c: %s at %s lost sync at byte %d\n",
psmouse->name, psmouse->phys, psmouse->pktcnt);
- if (++psmouse->out_of_sync == psmouse->resetafter) {
+ if (++psmouse->out_of_sync_cnt == psmouse->resetafter) {
__psmouse_set_state(psmouse, PSMOUSE_IGNORE);
printk(KERN_NOTICE "psmouse.c: issuing reconnect request\n");
serio_reconnect(psmouse->ps2dev.serio);
@@ -261,8 +262,8 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
case PSMOUSE_FULL_PACKET:
psmouse->pktcnt = 0;
- if (psmouse->out_of_sync) {
- psmouse->out_of_sync = 0;
+ if (psmouse->out_of_sync_cnt) {
+ psmouse->out_of_sync_cnt = 0;
printk(KERN_NOTICE "psmouse.c: %s at %s - driver resynched.\n",
psmouse->name, psmouse->phys);
}
@@ -408,7 +409,7 @@ int psmouse_reset(struct psmouse *psmouse)
/*
* Genius NetMouse magic init.
*/
-static int genius_detect(struct psmouse *psmouse, int set_properties)
+static int genius_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
@@ -424,9 +425,9 @@ static int genius_detect(struct psmouse *psmouse, int set_properties)
return -1;
if (set_properties) {
- set_bit(BTN_EXTRA, psmouse->dev->keybit);
- set_bit(BTN_SIDE, psmouse->dev->keybit);
- set_bit(REL_WHEEL, psmouse->dev->relbit);
+ __set_bit(BTN_EXTRA, psmouse->dev->keybit);
+ __set_bit(BTN_SIDE, psmouse->dev->keybit);
+ __set_bit(REL_WHEEL, psmouse->dev->relbit);
psmouse->vendor = "Genius";
psmouse->name = "Mouse";
@@ -439,7 +440,7 @@ static int genius_detect(struct psmouse *psmouse, int set_properties)
/*
* IntelliMouse magic init.
*/
-static int intellimouse_detect(struct psmouse *psmouse, int set_properties)
+static int intellimouse_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2];
@@ -456,8 +457,8 @@ static int intellimouse_detect(struct psmouse *psmouse, int set_properties)
return -1;
if (set_properties) {
- set_bit(BTN_MIDDLE, psmouse->dev->keybit);
- set_bit(REL_WHEEL, psmouse->dev->relbit);
+ __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+ __set_bit(REL_WHEEL, psmouse->dev->relbit);
if (!psmouse->vendor) psmouse->vendor = "Generic";
if (!psmouse->name) psmouse->name = "Wheel Mouse";
@@ -470,7 +471,7 @@ static int intellimouse_detect(struct psmouse *psmouse, int set_properties)
/*
* Try IntelliMouse/Explorer magic init.
*/
-static int im_explorer_detect(struct psmouse *psmouse, int set_properties)
+static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2];
@@ -497,11 +498,11 @@ static int im_explorer_detect(struct psmouse *psmouse, int set_properties)
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
if (set_properties) {
- set_bit(BTN_MIDDLE, psmouse->dev->keybit);
- set_bit(REL_WHEEL, psmouse->dev->relbit);
- set_bit(REL_HWHEEL, psmouse->dev->relbit);
- set_bit(BTN_SIDE, psmouse->dev->keybit);
- set_bit(BTN_EXTRA, psmouse->dev->keybit);
+ __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
+ __set_bit(REL_WHEEL, psmouse->dev->relbit);
+ __set_bit(REL_HWHEEL, psmouse->dev->relbit);
+ __set_bit(BTN_SIDE, psmouse->dev->keybit);
+ __set_bit(BTN_EXTRA, psmouse->dev->keybit);
if (!psmouse->vendor) psmouse->vendor = "Generic";
if (!psmouse->name) psmouse->name = "Explorer Mouse";
@@ -514,7 +515,7 @@ static int im_explorer_detect(struct psmouse *psmouse, int set_properties)
/*
* Kensington ThinkingMouse / ExpertMouse magic init.
*/
-static int thinking_detect(struct psmouse *psmouse, int set_properties)
+static int thinking_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2];
@@ -535,7 +536,7 @@ static int thinking_detect(struct psmouse *psmouse, int set_properties)
return -1;
if (set_properties) {
- set_bit(BTN_EXTRA, psmouse->dev->keybit);
+ __set_bit(BTN_EXTRA, psmouse->dev->keybit);
psmouse->vendor = "Kensington";
psmouse->name = "ThinkingMouse";
@@ -547,7 +548,7 @@ static int thinking_detect(struct psmouse *psmouse, int set_properties)
/*
* Bare PS/2 protocol "detection". Always succeeds.
*/
-static int ps2bare_detect(struct psmouse *psmouse, int set_properties)
+static int ps2bare_detect(struct psmouse *psmouse, bool set_properties)
{
if (set_properties) {
if (!psmouse->vendor) psmouse->vendor = "Generic";
@@ -561,12 +562,12 @@ static int ps2bare_detect(struct psmouse *psmouse, int set_properties)
* Cortron PS/2 protocol detection. There's no special way to detect it, so it
* must be forced by sysfs protocol writing.
*/
-static int cortron_detect(struct psmouse *psmouse, int set_properties)
+static int cortron_detect(struct psmouse *psmouse, bool set_properties)
{
if (set_properties) {
psmouse->vendor = "Cortron";
psmouse->name = "PS/2 Trackball";
- set_bit(BTN_SIDE, psmouse->dev->keybit);
+ __set_bit(BTN_SIDE, psmouse->dev->keybit);
}
return 0;
@@ -578,9 +579,9 @@ static int cortron_detect(struct psmouse *psmouse, int set_properties)
*/
static int psmouse_extensions(struct psmouse *psmouse,
- unsigned int max_proto, int set_properties)
+ unsigned int max_proto, bool set_properties)
{
- int synaptics_hardware = 0;
+ bool synaptics_hardware = true;
/*
* We always check for lifebook because it does not disturb mouse
@@ -607,7 +608,7 @@ static int psmouse_extensions(struct psmouse *psmouse,
* can reset it properly after probing for intellimouse.
*/
if (max_proto > PSMOUSE_PS2 && synaptics_detect(psmouse, set_properties) == 0) {
- synaptics_hardware = 1;
+ synaptics_hardware = true;
if (max_proto > PSMOUSE_IMEX) {
if (!set_properties || synaptics_init(psmouse) == 0)
@@ -666,6 +667,20 @@ static int psmouse_extensions(struct psmouse *psmouse,
max_proto = PSMOUSE_IMEX;
}
+/*
+ * Try Finger Sensing Pad
+ */
+ if (max_proto > PSMOUSE_IMEX) {
+ if (fsp_detect(psmouse, set_properties) == 0) {
+ if (!set_properties || fsp_init(psmouse) == 0)
+ return PSMOUSE_FSP;
+/*
+ * Init failed, try basic relative protocols
+ */
+ max_proto = PSMOUSE_IMEX;
+ }
+ }
+
if (max_proto > PSMOUSE_IMEX) {
if (genius_detect(psmouse, set_properties) == 0)
return PSMOUSE_GENPS;
@@ -718,7 +733,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.type = PSMOUSE_PS2,
.name = "PS/2",
.alias = "bare",
- .maxproto = 1,
+ .maxproto = true,
.detect = ps2bare_detect,
},
#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
@@ -745,14 +760,14 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.type = PSMOUSE_IMPS,
.name = "ImPS/2",
.alias = "imps",
- .maxproto = 1,
+ .maxproto = true,
.detect = intellimouse_detect,
},
{
.type = PSMOUSE_IMEX,
.name = "ImExPS/2",
.alias = "exps",
- .maxproto = 1,
+ .maxproto = true,
.detect = im_explorer_detect,
},
#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
@@ -813,7 +828,16 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.detect = elantech_detect,
.init = elantech_init,
},
- #endif
+#endif
+#ifdef CONFIG_MOUSE_PS2_SENTELIC
+ {
+ .type = PSMOUSE_FSP,
+ .name = "FSPPS/2",
+ .alias = "fsp",
+ .detect = fsp_detect,
+ .init = fsp_init,
+ },
+#endif
{
.type = PSMOUSE_CORTRON,
.name = "CortronPS/2",
@@ -824,7 +848,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.type = PSMOUSE_AUTO,
.name = "auto",
.alias = "any",
- .maxproto = 1,
+ .maxproto = true,
},
};
@@ -990,7 +1014,7 @@ static void psmouse_resync(struct work_struct *work)
container_of(work, struct psmouse, resync_work.work);
struct serio *serio = psmouse->ps2dev.serio;
psmouse_ret_t rc = PSMOUSE_GOOD_DATA;
- int failed = 0, enabled = 0;
+ bool failed = false, enabled = false;
int i;
mutex_lock(&psmouse_mutex);
@@ -1017,9 +1041,9 @@ static void psmouse_resync(struct work_struct *work)
if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) {
if (psmouse->num_resyncs < 3 || psmouse->acks_disable_command)
- failed = 1;
+ failed = true;
} else
- psmouse->acks_disable_command = 1;
+ psmouse->acks_disable_command = true;
/*
* Poll the mouse. If it was reset the packet will be shorter than
@@ -1030,7 +1054,7 @@ static void psmouse_resync(struct work_struct *work)
*/
if (!failed) {
if (psmouse->poll(psmouse))
- failed = 1;
+ failed = true;
else {
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
for (i = 0; i < psmouse->pktsize; i++) {
@@ -1040,7 +1064,7 @@ static void psmouse_resync(struct work_struct *work)
break;
}
if (rc != PSMOUSE_FULL_PACKET)
- failed = 1;
+ failed = true;
psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
}
}
@@ -1051,7 +1075,7 @@ static void psmouse_resync(struct work_struct *work)
*/
for (i = 0; i < 5; i++) {
if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
- enabled = 1;
+ enabled = true;
break;
}
msleep(200);
@@ -1060,7 +1084,7 @@ static void psmouse_resync(struct work_struct *work)
if (!enabled) {
printk(KERN_WARNING "psmouse.c: failed to re-enable mouse on %s\n",
psmouse->ps2dev.serio->phys);
- failed = 1;
+ failed = true;
}
if (failed) {
@@ -1187,7 +1211,8 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse
psmouse->type = proto->type;
}
else
- psmouse->type = psmouse_extensions(psmouse, psmouse_max_proto, 1);
+ psmouse->type = psmouse_extensions(psmouse,
+ psmouse_max_proto, true);
/*
* If mouse's packet size is 3 there is no point in polling the
@@ -1342,8 +1367,10 @@ static int psmouse_reconnect(struct serio *serio)
if (psmouse->reconnect(psmouse))
goto out;
} else if (psmouse_probe(psmouse) < 0 ||
- psmouse->type != psmouse_extensions(psmouse, psmouse_max_proto, 0))
+ psmouse->type != psmouse_extensions(psmouse,
+ psmouse_max_proto, false)) {
goto out;
+ }
/* ok, the device type (and capabilities) match the old one,
* we can continue using it, complete intialization
@@ -1528,7 +1555,9 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
while (serio->child) {
if (++retry > 3) {
- printk(KERN_WARNING "psmouse: failed to destroy child port, protocol change aborted.\n");
+ printk(KERN_WARNING
+ "psmouse: failed to destroy child port, "
+ "protocol change aborted.\n");
input_free_device(new_dev);
return -EIO;
}
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 54ed267894b..e053bdd137f 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -47,10 +47,10 @@ struct psmouse {
unsigned char pktcnt;
unsigned char pktsize;
unsigned char type;
- unsigned char acks_disable_command;
+ bool acks_disable_command;
unsigned int model;
unsigned long last;
- unsigned long out_of_sync;
+ unsigned long out_of_sync_cnt;
unsigned long num_resyncs;
enum psmouse_state state;
char devname[64];
@@ -60,7 +60,7 @@ struct psmouse {
unsigned int resolution;
unsigned int resetafter;
unsigned int resync_time;
- unsigned int smartscroll; /* Logitech only */
+ bool smartscroll; /* Logitech only */
psmouse_ret_t (*protocol_handler)(struct psmouse *psmouse);
void (*set_rate)(struct psmouse *psmouse, unsigned int rate);
@@ -91,6 +91,7 @@ enum psmouse_type {
PSMOUSE_CORTRON,
PSMOUSE_HGPK,
PSMOUSE_ELANTECH,
+ PSMOUSE_FSP,
PSMOUSE_AUTO /* This one should always be last */
};
@@ -107,7 +108,7 @@ struct psmouse_attribute {
ssize_t (*show)(struct psmouse *psmouse, void *data, char *buf);
ssize_t (*set)(struct psmouse *psmouse, void *data,
const char *buf, size_t count);
- int protect;
+ bool protect;
};
#define to_psmouse_attr(a) container_of((a), struct psmouse_attribute, dattr)
@@ -116,9 +117,7 @@ ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *at
ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
-#define __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, _protect) \
-static ssize_t _show(struct psmouse *, void *data, char *); \
-static ssize_t _set(struct psmouse *, void *data, const char *, size_t); \
+#define __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, _set, _protect) \
static struct psmouse_attribute psmouse_attr_##_name = { \
.dattr = { \
.attr = { \
@@ -134,7 +133,20 @@ static struct psmouse_attribute psmouse_attr_##_name = { \
.protect = _protect, \
}
-#define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set) \
- __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, 1)
+#define __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, _protect) \
+ static ssize_t _show(struct psmouse *, void *, char *); \
+ static ssize_t _set(struct psmouse *, void *, const char *, size_t); \
+ __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, _set, _protect)
+
+#define PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set) \
+ __PSMOUSE_DEFINE_ATTR(_name, _mode, _data, _show, _set, true)
+
+#define PSMOUSE_DEFINE_RO_ATTR(_name, _mode, _data, _show) \
+ static ssize_t _show(struct psmouse *, void *, char *); \
+ __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, _show, NULL, true)
+
+#define PSMOUSE_DEFINE_WO_ATTR(_name, _mode, _data, _set) \
+ static ssize_t _set(struct psmouse *, void *, const char *, size_t); \
+ __PSMOUSE_DEFINE_ATTR_VAR(_name, _mode, _data, NULL, _set, true)
#endif /* _PSMOUSE_H */
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
new file mode 100644
index 00000000000..84e2fc04d11
--- /dev/null
+++ b/drivers/input/mouse/sentelic.c
@@ -0,0 +1,867 @@
+/*-
+ * Finger Sensing Pad PS/2 mouse driver.
+ *
+ * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
+ * Copyright (C) 2005-2009 Tai-hwa Liang, Sentelic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/input.h>
+#include <linux/ctype.h>
+#include <linux/libps2.h>
+#include <linux/serio.h>
+#include <linux/jiffies.h>
+
+#include "psmouse.h"
+#include "sentelic.h"
+
+/*
+ * Timeout for FSP PS/2 command only (in milliseconds).
+ */
+#define FSP_CMD_TIMEOUT 200
+#define FSP_CMD_TIMEOUT2 30
+
+/** Driver version. */
+static const char fsp_drv_ver[] = "1.0.0-K";
+
+/*
+ * Make sure that the value being sent to FSP will not conflict with
+ * possible sample rate values.
+ */
+static unsigned char fsp_test_swap_cmd(unsigned char reg_val)
+{
+ switch (reg_val) {
+ case 10: case 20: case 40: case 60: case 80: case 100: case 200:
+ /*
+ * The requested value being sent to FSP matched to possible
+ * sample rates, swap the given value such that the hardware
+ * wouldn't get confused.
+ */
+ return (reg_val >> 4) | (reg_val << 4);
+ default:
+ return reg_val; /* swap isn't necessary */
+ }
+}
+
+/*
+ * Make sure that the value being sent to FSP will not conflict with certain
+ * commands.
+ */
+static unsigned char fsp_test_invert_cmd(unsigned char reg_val)
+{
+ switch (reg_val) {
+ case 0xe9: case 0xee: case 0xf2: case 0xff:
+ /*
+ * The requested value being sent to FSP matched to certain
+ * commands, inverse the given value such that the hardware
+ * wouldn't get confused.
+ */
+ return ~reg_val;
+ default:
+ return reg_val; /* inversion isn't necessary */
+ }
+}
+
+static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[3];
+ unsigned char addr;
+ int rc = -1;
+
+ /*
+ * We need to shut off the device and switch it into command
+ * mode so we don't confuse our protocol handler. We don't need
+ * to do that for writes because sysfs set helper does this for
+ * us.
+ */
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE);
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ mutex_lock(&ps2dev->cmd_mutex);
+
+ if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+ goto out;
+
+ /* should return 0xfe(request for resending) */
+ ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
+ /* should return 0xfc(failed) */
+ ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
+
+ if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+ goto out;
+
+ if ((addr = fsp_test_invert_cmd(reg_addr)) != reg_addr) {
+ ps2_sendbyte(ps2dev, 0x68, FSP_CMD_TIMEOUT2);
+ } else if ((addr = fsp_test_swap_cmd(reg_addr)) != reg_addr) {
+ /* swapping is required */
+ ps2_sendbyte(ps2dev, 0xcc, FSP_CMD_TIMEOUT2);
+ /* expect 0xfe */
+ } else {
+ /* swapping isn't necessary */
+ ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
+ /* expect 0xfe */
+ }
+ /* should return 0xfc(failed) */
+ ps2_sendbyte(ps2dev, addr, FSP_CMD_TIMEOUT);
+
+ if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) < 0)
+ goto out;
+
+ *reg_val = param[2];
+ rc = 0;
+
+ out:
+ mutex_unlock(&ps2dev->cmd_mutex);
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+ psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+ dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
+ reg_addr, *reg_val, rc);
+ return rc;
+}
+
+static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char v;
+ int rc = -1;
+
+ mutex_lock(&ps2dev->cmd_mutex);
+
+ if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+ goto out;
+
+ if ((v = fsp_test_invert_cmd(reg_addr)) != reg_addr) {
+ /* inversion is required */
+ ps2_sendbyte(ps2dev, 0x74, FSP_CMD_TIMEOUT2);
+ } else {
+ if ((v = fsp_test_swap_cmd(reg_addr)) != reg_addr) {
+ /* swapping is required */
+ ps2_sendbyte(ps2dev, 0x77, FSP_CMD_TIMEOUT2);
+ } else {
+ /* swapping isn't necessary */
+ ps2_sendbyte(ps2dev, 0x55, FSP_CMD_TIMEOUT2);
+ }
+ }
+ /* write the register address in correct order */
+ ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
+
+ if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+ return -1;
+
+ if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {
+ /* inversion is required */
+ ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2);
+ } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) {
+ /* swapping is required */
+ ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2);
+ } else {
+ /* swapping isn't necessary */
+ ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2);
+ }
+
+ /* write the register value in correct order */
+ ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
+ rc = 0;
+
+ out:
+ mutex_unlock(&ps2dev->cmd_mutex);
+ dev_dbg(&ps2dev->serio->dev, "WRITE REG: 0x%02x to 0x%02x (rc = %d)\n",
+ reg_addr, reg_val, rc);
+ return rc;
+}
+
+/* Enable register clock gating for writing certain registers */
+static int fsp_reg_write_enable(struct psmouse *psmouse, bool enable)
+{
+ int v, nv;
+
+ if (fsp_reg_read(psmouse, FSP_REG_SYSCTL1, &v) == -1)
+ return -1;
+
+ if (enable)
+ nv = v | FSP_BIT_EN_REG_CLK;
+ else
+ nv = v & ~FSP_BIT_EN_REG_CLK;
+
+ /* only write if necessary */
+ if (nv != v)
+ if (fsp_reg_write(psmouse, FSP_REG_SYSCTL1, nv) == -1)
+ return -1;
+
+ return 0;
+}
+
+static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[3];
+ int rc = -1;
+
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE);
+ psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ mutex_lock(&ps2dev->cmd_mutex);
+
+ if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+ goto out;
+
+ ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
+ ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
+
+ if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+ goto out;
+
+ ps2_sendbyte(ps2dev, 0x83, FSP_CMD_TIMEOUT2);
+ ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
+
+ /* get the returned result */
+ if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO))
+ goto out;
+
+ *reg_val = param[2];
+ rc = 0;
+
+ out:
+ mutex_unlock(&ps2dev->cmd_mutex);
+ ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
+ psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+ dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n",
+ *reg_val, rc);
+ return rc;
+}
+
+static int fsp_page_reg_write(struct psmouse *psmouse, int reg_val)
+{
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char v;
+ int rc = -1;
+
+ mutex_lock(&ps2dev->cmd_mutex);
+
+ if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+ goto out;
+
+ ps2_sendbyte(ps2dev, 0x38, FSP_CMD_TIMEOUT2);
+ ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
+
+ if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
+ return -1;
+
+ if ((v = fsp_test_invert_cmd(reg_val)) != reg_val) {
+ ps2_sendbyte(ps2dev, 0x47, FSP_CMD_TIMEOUT2);
+ } else if ((v = fsp_test_swap_cmd(reg_val)) != reg_val) {
+ /* swapping is required */
+ ps2_sendbyte(ps2dev, 0x44, FSP_CMD_TIMEOUT2);
+ } else {
+ /* swapping isn't necessary */
+ ps2_sendbyte(ps2dev, 0x33, FSP_CMD_TIMEOUT2);
+ }
+
+ ps2_sendbyte(ps2dev, v, FSP_CMD_TIMEOUT2);
+ rc = 0;
+
+ out:
+ mutex_unlock(&ps2dev->cmd_mutex);
+ dev_dbg(&ps2dev->serio->dev, "WRITE PAGE REG: to 0x%02x (rc = %d)\n",
+ reg_val, rc);
+ return rc;
+}
+
+static int fsp_get_version(struct psmouse *psmouse, int *version)
+{
+ if (fsp_reg_read(psmouse, FSP_REG_VERSION, version))
+ return -EIO;
+
+ return 0;
+}
+
+static int fsp_get_revision(struct psmouse *psmouse, int *rev)
+{
+ if (fsp_reg_read(psmouse, FSP_REG_REVISION, rev))
+ return -EIO;
+
+ return 0;
+}
+
+static int fsp_get_buttons(struct psmouse *psmouse, int *btn)
+{
+ static const int buttons[] = {
+ 0x16, /* Left/Middle/Right/Forward/Backward & Scroll Up/Down */
+ 0x06, /* Left/Middle/Right & Scroll Up/Down/Right/Left */
+ 0x04, /* Left/Middle/Right & Scroll Up/Down */
+ 0x02, /* Left/Middle/Right */
+ };
+ int val;
+
+ if (fsp_reg_read(psmouse, FSP_REG_TMOD_STATUS1, &val) == -1)
+ return -EIO;
+
+ *btn = buttons[(val & 0x30) >> 4];
+ return 0;
+}
+
+/* Enable on-pad command tag output */
+static int fsp_opc_tag_enable(struct psmouse *psmouse, bool enable)
+{
+ int v, nv;
+ int res = 0;
+
+ if (fsp_reg_read(psmouse, FSP_REG_OPC_QDOWN, &v) == -1) {
+ dev_err(&psmouse->ps2dev.serio->dev, "Unable get OPC state.\n");
+ return -EIO;
+ }
+
+ if (enable)
+ nv = v | FSP_BIT_EN_OPC_TAG;
+ else
+ nv = v & ~FSP_BIT_EN_OPC_TAG;
+
+ /* only write if necessary */
+ if (nv != v) {
+ fsp_reg_write_enable(psmouse, true);
+ res = fsp_reg_write(psmouse, FSP_REG_OPC_QDOWN, nv);
+ fsp_reg_write_enable(psmouse, false);
+ }
+
+ if (res != 0) {
+ dev_err(&psmouse->ps2dev.serio->dev,
+ "Unable to enable OPC tag.\n");
+ res = -EIO;
+ }
+
+ return res;
+}
+
+static int fsp_onpad_vscr(struct psmouse *psmouse, bool enable)
+{
+ struct fsp_data *pad = psmouse->private;
+ int val;
+
+ if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val))
+ return -EIO;
+
+ pad->vscroll = enable;
+
+ if (enable)
+ val |= (FSP_BIT_FIX_VSCR | FSP_BIT_ONPAD_ENABLE);
+ else
+ val &= ~FSP_BIT_FIX_VSCR;
+
+ if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val))
+ return -EIO;
+
+ return 0;
+}
+
+static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable)
+{
+ struct fsp_data *pad = psmouse->private;
+ int val, v2;
+
+ if (fsp_reg_read(psmouse, FSP_REG_ONPAD_CTL, &val))
+ return -EIO;
+
+ if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &v2))
+ return -EIO;
+
+ pad->hscroll = enable;
+
+ if (enable) {
+ val |= (FSP_BIT_FIX_HSCR | FSP_BIT_ONPAD_ENABLE);
+ v2 |= FSP_BIT_EN_MSID6;
+ } else {
+ val &= ~FSP_BIT_FIX_HSCR;
+ v2 &= ~(FSP_BIT_EN_MSID6 | FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8);
+ }
+
+ if (fsp_reg_write(psmouse, FSP_REG_ONPAD_CTL, val))
+ return -EIO;
+
+ /* reconfigure horizontal scrolling packet output */
+ if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, v2))
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Write device specific initial parameters.
+ *
+ * ex: 0xab 0xcd - write oxcd into register 0xab
+ */
+static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,
+ const char *buf, size_t count)
+{
+ unsigned long reg, val;
+ char *rest;
+ ssize_t retval;
+
+ reg = simple_strtoul(buf, &rest, 16);
+ if (rest == buf || *rest != ' ' || reg > 0xff)
+ return -EINVAL;
+
+ if (strict_strtoul(rest + 1, 16, &val) || val > 0xff)
+ return -EINVAL;
+
+ if (fsp_reg_write_enable(psmouse, true))
+ return -EIO;
+
+ retval = fsp_reg_write(psmouse, reg, val) < 0 ? -EIO : count;
+
+ fsp_reg_write_enable(psmouse, false);
+
+ return count;
+}
+
+PSMOUSE_DEFINE_WO_ATTR(setreg, S_IWUSR, NULL, fsp_attr_set_setreg);
+
+static ssize_t fsp_attr_show_getreg(struct psmouse *psmouse,
+ void *data, char *buf)
+{
+ struct fsp_data *pad = psmouse->private;
+
+ return sprintf(buf, "%02x%02x\n", pad->last_reg, pad->last_val);
+}
+
+/*
+ * Read a register from device.
+ *
+ * ex: 0xab -- read content from register 0xab
+ */
+static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data,
+ const char *buf, size_t count)
+{
+ struct fsp_data *pad = psmouse->private;
+ unsigned long reg;
+ int val;
+
+ if (strict_strtoul(buf, 16, &reg) || reg > 0xff)
+ return -EINVAL;
+
+ if (fsp_reg_read(psmouse, reg, &val))
+ return -EIO;
+
+ pad->last_reg = reg;
+ pad->last_val = val;
+
+ return count;
+}
+
+PSMOUSE_DEFINE_ATTR(getreg, S_IWUSR | S_IRUGO, NULL,
+ fsp_attr_show_getreg, fsp_attr_set_getreg);
+
+static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse,
+ void *data, char *buf)
+{
+ int val = 0;
+
+ if (fsp_page_reg_read(psmouse, &val))
+ return -EIO;
+
+ return sprintf(buf, "%02x\n", val);
+}
+
+static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+
+ if (strict_strtoul(buf, 16, &val) || val > 0xff)
+ return -EINVAL;
+
+ if (fsp_page_reg_write(psmouse, val))
+ return -EIO;
+
+ return count;
+}
+
+PSMOUSE_DEFINE_ATTR(page, S_IWUSR | S_IRUGO, NULL,
+ fsp_attr_show_pagereg, fsp_attr_set_pagereg);
+
+static ssize_t fsp_attr_show_vscroll(struct psmouse *psmouse,
+ void *data, char *buf)
+{
+ struct fsp_data *pad = psmouse->private;
+
+ return sprintf(buf, "%d\n", pad->vscroll);
+}
+
+static ssize_t fsp_attr_set_vscroll(struct psmouse *psmouse, void *data,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val) || val > 1)
+ return -EINVAL;
+
+ fsp_onpad_vscr(psmouse, val);
+
+ return count;
+}
+
+PSMOUSE_DEFINE_ATTR(vscroll, S_IWUSR | S_IRUGO, NULL,
+ fsp_attr_show_vscroll, fsp_attr_set_vscroll);
+
+static ssize_t fsp_attr_show_hscroll(struct psmouse *psmouse,
+ void *data, char *buf)
+{
+ struct fsp_data *pad = psmouse->private;
+
+ return sprintf(buf, "%d\n", pad->hscroll);
+}
+
+static ssize_t fsp_attr_set_hscroll(struct psmouse *psmouse, void *data,
+ const char *buf, size_t count)
+{
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val) || val > 1)
+ return -EINVAL;
+
+ fsp_onpad_hscr(psmouse, val);
+
+ return count;
+}
+
+PSMOUSE_DEFINE_ATTR(hscroll, S_IWUSR | S_IRUGO, NULL,
+ fsp_attr_show_hscroll, fsp_attr_set_hscroll);
+
+static ssize_t fsp_attr_show_flags(struct psmouse *psmouse,
+ void *data, char *buf)
+{
+ struct fsp_data *pad = psmouse->private;
+
+ return sprintf(buf, "%c\n",
+ pad->flags & FSPDRV_FLAG_EN_OPC ? 'C' : 'c');
+}
+
+static ssize_t fsp_attr_set_flags(struct psmouse *psmouse, void *data,
+ const char *buf, size_t count)
+{
+ struct fsp_data *pad = psmouse->private;
+ size_t i;
+
+ for (i = 0; i < count; i++) {
+ switch (buf[i]) {
+ case 'C':
+ pad->flags |= FSPDRV_FLAG_EN_OPC;
+ break;
+ case 'c':
+ pad->flags &= ~FSPDRV_FLAG_EN_OPC;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+ return count;
+}
+
+PSMOUSE_DEFINE_ATTR(flags, S_IWUSR | S_IRUGO, NULL,
+ fsp_attr_show_flags, fsp_attr_set_flags);
+
+static ssize_t fsp_attr_show_ver(struct psmouse *psmouse,
+ void *data, char *buf)
+{
+ return sprintf(buf, "Sentelic FSP kernel module %s\n", fsp_drv_ver);
+}
+
+PSMOUSE_DEFINE_RO_ATTR(ver, S_IRUGO, NULL, fsp_attr_show_ver);
+
+static struct attribute *fsp_attributes[] = {
+ &psmouse_attr_setreg.dattr.attr,
+ &psmouse_attr_getreg.dattr.attr,
+ &psmouse_attr_page.dattr.attr,
+ &psmouse_attr_vscroll.dattr.attr,
+ &psmouse_attr_hscroll.dattr.attr,
+ &psmouse_attr_flags.dattr.attr,
+ &psmouse_attr_ver.dattr.attr,
+ NULL
+};
+
+static struct attribute_group fsp_attribute_group = {
+ .attrs = fsp_attributes,
+};
+
+#ifdef FSP_DEBUG
+static void fsp_packet_debug(unsigned char packet[])
+{
+ static unsigned int ps2_packet_cnt;
+ static unsigned int ps2_last_second;
+ unsigned int jiffies_msec;
+
+ ps2_packet_cnt++;
+ jiffies_msec = jiffies_to_msecs(jiffies);
+ printk(KERN_DEBUG "%08dms PS/2 packets: %02x, %02x, %02x, %02x\n",
+ jiffies_msec, packet[0], packet[1], packet[2], packet[3]);
+
+ if (jiffies_msec - ps2_last_second > 1000) {
+ printk(KERN_DEBUG "PS/2 packets/sec = %d\n", ps2_packet_cnt);
+ ps2_packet_cnt = 0;
+ ps2_last_second = jiffies_msec;
+ }
+}
+#else
+static void fsp_packet_debug(unsigned char packet[])
+{
+}
+#endif
+
+static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct fsp_data *ad = psmouse->private;
+ unsigned char *packet = psmouse->packet;
+ unsigned char button_status = 0, lscroll = 0, rscroll = 0;
+ int rel_x, rel_y;
+
+ if (psmouse->pktcnt < 4)
+ return PSMOUSE_GOOD_DATA;
+
+ /*
+ * Full packet accumulated, process it
+ */
+
+ switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) {
+ case FSP_PKT_TYPE_ABS:
+ dev_warn(&psmouse->ps2dev.serio->dev,
+ "Unexpected absolute mode packet, ignored.\n");
+ break;
+
+ case FSP_PKT_TYPE_NORMAL_OPC:
+ /* on-pad click, filter it if necessary */
+ if ((ad->flags & FSPDRV_FLAG_EN_OPC) != FSPDRV_FLAG_EN_OPC)
+ packet[0] &= ~BIT(0);
+ /* fall through */
+
+ case FSP_PKT_TYPE_NORMAL:
+ /* normal packet */
+ /* special packet data translation from on-pad packets */
+ if (packet[3] != 0) {
+ if (packet[3] & BIT(0))
+ button_status |= 0x01; /* wheel down */
+ if (packet[3] & BIT(1))
+ button_status |= 0x0f; /* wheel up */
+ if (packet[3] & BIT(2))
+ button_status |= BIT(5);/* horizontal left */
+ if (packet[3] & BIT(3))
+ button_status |= BIT(4);/* horizontal right */
+ /* push back to packet queue */
+ if (button_status != 0)
+ packet[3] = button_status;
+ rscroll = (packet[3] >> 4) & 1;
+ lscroll = (packet[3] >> 5) & 1;
+ }
+ /*
+ * Processing wheel up/down and extra button events
+ */
+ input_report_rel(dev, REL_WHEEL,
+ (int)(packet[3] & 8) - (int)(packet[3] & 7));
+ input_report_rel(dev, REL_HWHEEL, lscroll - rscroll);
+ input_report_key(dev, BTN_BACK, lscroll);
+ input_report_key(dev, BTN_FORWARD, rscroll);
+
+ /*
+ * Standard PS/2 Mouse
+ */
+ input_report_key(dev, BTN_LEFT, packet[0] & 1);
+ input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
+ input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
+
+ rel_x = packet[1] ? (int)packet[1] - (int)((packet[0] << 4) & 0x100) : 0;
+ rel_y = packet[2] ? (int)((packet[0] << 3) & 0x100) - (int)packet[2] : 0;
+
+ input_report_rel(dev, REL_X, rel_x);
+ input_report_rel(dev, REL_Y, rel_y);
+ break;
+ }
+
+ input_sync(dev);
+
+ fsp_packet_debug(packet);
+
+ return PSMOUSE_FULL_PACKET;
+}
+
+static int fsp_activate_protocol(struct psmouse *psmouse)
+{
+ struct fsp_data *pad = psmouse->private;
+ struct ps2dev *ps2dev = &psmouse->ps2dev;
+ unsigned char param[2];
+ int val;
+
+ /*
+ * Standard procedure to enter FSP Intellimouse mode
+ * (scrolling wheel, 4th and 5th buttons)
+ */
+ param[0] = 200;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 200;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 80;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+
+ ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
+ if (param[0] != 0x04) {
+ dev_err(&psmouse->ps2dev.serio->dev,
+ "Unable to enable 4 bytes packet format.\n");
+ return -EIO;
+ }
+
+ if (fsp_reg_read(psmouse, FSP_REG_SYSCTL5, &val)) {
+ dev_err(&psmouse->ps2dev.serio->dev,
+ "Unable to read SYSCTL5 register.\n");
+ return -EIO;
+ }
+
+ val &= ~(FSP_BIT_EN_MSID7 | FSP_BIT_EN_MSID8 | FSP_BIT_EN_AUTO_MSID8);
+ /* Ensure we are not in absolute mode */
+ val &= ~FSP_BIT_EN_PKT_G0;
+ if (pad->buttons == 0x06) {
+ /* Left/Middle/Right & Scroll Up/Down/Right/Left */
+ val |= FSP_BIT_EN_MSID6;
+ }
+
+ if (fsp_reg_write(psmouse, FSP_REG_SYSCTL5, val)) {
+ dev_err(&psmouse->ps2dev.serio->dev,
+ "Unable to set up required mode bits.\n");
+ return -EIO;
+ }
+
+ /*
+ * Enable OPC tags such that driver can tell the difference between
+ * on-pad and real button click
+ */
+ if (fsp_opc_tag_enable(psmouse, true))
+ dev_warn(&psmouse->ps2dev.serio->dev,
+ "Failed to enable OPC tag mode.\n");
+
+ /* Enable on-pad vertical and horizontal scrolling */
+ fsp_onpad_vscr(psmouse, true);
+ fsp_onpad_hscr(psmouse, true);
+
+ return 0;
+}
+
+int fsp_detect(struct psmouse *psmouse, bool set_properties)
+{
+ int id;
+
+ if (fsp_reg_read(psmouse, FSP_REG_DEVICE_ID, &id))
+ return -EIO;
+
+ if (id != 0x01)
+ return -ENODEV;
+
+ if (set_properties) {
+ psmouse->vendor = "Sentelic";
+ psmouse->name = "FingerSensingPad";
+ }
+
+ return 0;
+}
+
+static void fsp_reset(struct psmouse *psmouse)
+{
+ fsp_opc_tag_enable(psmouse, false);
+ fsp_onpad_vscr(psmouse, false);
+ fsp_onpad_hscr(psmouse, false);
+}
+
+static void fsp_disconnect(struct psmouse *psmouse)
+{
+ sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
+ &fsp_attribute_group);
+
+ fsp_reset(psmouse);
+ kfree(psmouse->private);
+}
+
+static int fsp_reconnect(struct psmouse *psmouse)
+{
+ int version;
+
+ if (fsp_detect(psmouse, 0))
+ return -ENODEV;
+
+ if (fsp_get_version(psmouse, &version))
+ return -ENODEV;
+
+ if (fsp_activate_protocol(psmouse))
+ return -EIO;
+
+ return 0;
+}
+
+int fsp_init(struct psmouse *psmouse)
+{
+ struct fsp_data *priv;
+ int ver, rev, buttons;
+ int error;
+
+ if (fsp_get_version(psmouse, &ver) ||
+ fsp_get_revision(psmouse, &rev) ||
+ fsp_get_buttons(psmouse, &buttons)) {
+ return -ENODEV;
+ }
+
+ printk(KERN_INFO
+ "Finger Sensing Pad, hw: %d.%d.%d, sw: %s, buttons: %d\n",
+ ver >> 4, ver & 0x0F, rev, fsp_drv_ver, buttons & 7);
+
+ psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->ver = ver;
+ priv->rev = rev;
+ priv->buttons = buttons;
+
+ /* enable on-pad click by default */
+ priv->flags |= FSPDRV_FLAG_EN_OPC;
+
+ /* Set up various supported input event bits */
+ __set_bit(BTN_BACK, psmouse->dev->keybit);
+ __set_bit(BTN_FORWARD, psmouse->dev->keybit);
+ __set_bit(REL_WHEEL, psmouse->dev->relbit);
+ __set_bit(REL_HWHEEL, psmouse->dev->relbit);
+
+ psmouse->protocol_handler = fsp_process_byte;
+ psmouse->disconnect = fsp_disconnect;
+ psmouse->reconnect = fsp_reconnect;
+ psmouse->cleanup = fsp_reset;
+ psmouse->pktsize = 4;
+
+ /* set default packet output based on number of buttons we found */
+ error = fsp_activate_protocol(psmouse);
+ if (error)
+ goto err_out;
+
+ error = sysfs_create_group(&psmouse->ps2dev.serio->dev.kobj,
+ &fsp_attribute_group);
+ if (error) {
+ dev_err(&psmouse->ps2dev.serio->dev,
+ "Failed to create sysfs attributes (%d)", error);
+ goto err_out;
+ }
+
+ return 0;
+
+ err_out:
+ kfree(psmouse->private);
+ psmouse->private = NULL;
+ return error;
+}
diff --git a/drivers/input/mouse/sentelic.h b/drivers/input/mouse/sentelic.h
new file mode 100644
index 00000000000..ed1395ac7b8
--- /dev/null
+++ b/drivers/input/mouse/sentelic.h
@@ -0,0 +1,98 @@
+/*-
+ * Finger Sensing Pad PS/2 mouse driver.
+ *
+ * Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
+ * Copyright (C) 2005-2009 Tai-hwa Liang, Sentelic Corporation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __SENTELIC_H
+#define __SENTELIC_H
+
+/* Finger-sensing Pad information registers */
+#define FSP_REG_DEVICE_ID 0x00
+#define FSP_REG_VERSION 0x01
+#define FSP_REG_REVISION 0x04
+#define FSP_REG_TMOD_STATUS1 0x0B
+#define FSP_BIT_NO_ROTATION BIT(3)
+#define FSP_REG_PAGE_CTRL 0x0F
+
+/* Finger-sensing Pad control registers */
+#define FSP_REG_SYSCTL1 0x10
+#define FSP_BIT_EN_REG_CLK BIT(5)
+#define FSP_REG_OPC_QDOWN 0x31
+#define FSP_BIT_EN_OPC_TAG BIT(7)
+#define FSP_REG_OPTZ_XLO 0x34
+#define FSP_REG_OPTZ_XHI 0x35
+#define FSP_REG_OPTZ_YLO 0x36
+#define FSP_REG_OPTZ_YHI 0x37
+#define FSP_REG_SYSCTL5 0x40
+#define FSP_BIT_90_DEGREE BIT(0)
+#define FSP_BIT_EN_MSID6 BIT(1)
+#define FSP_BIT_EN_MSID7 BIT(2)
+#define FSP_BIT_EN_MSID8 BIT(3)
+#define FSP_BIT_EN_AUTO_MSID8 BIT(5)
+#define FSP_BIT_EN_PKT_G0 BIT(6)
+
+#define FSP_REG_ONPAD_CTL 0x43
+#define FSP_BIT_ONPAD_ENABLE BIT(0)
+#define FSP_BIT_ONPAD_FBBB BIT(1)
+#define FSP_BIT_FIX_VSCR BIT(3)
+#define FSP_BIT_FIX_HSCR BIT(5)
+#define FSP_BIT_DRAG_LOCK BIT(6)
+
+/* Finger-sensing Pad packet formating related definitions */
+
+/* absolute packet type */
+#define FSP_PKT_TYPE_NORMAL (0x00)
+#define FSP_PKT_TYPE_ABS (0x01)
+#define FSP_PKT_TYPE_NOTIFY (0x02)
+#define FSP_PKT_TYPE_NORMAL_OPC (0x03)
+#define FSP_PKT_TYPE_SHIFT (6)
+
+#ifdef __KERNEL__
+
+struct fsp_data {
+ unsigned char ver; /* hardware version */
+ unsigned char rev; /* hardware revison */
+ unsigned char buttons; /* Number of buttons */
+ unsigned int flags;
+#define FSPDRV_FLAG_EN_OPC (0x001) /* enable on-pad clicking */
+
+ bool vscroll; /* Vertical scroll zone enabled */
+ bool hscroll; /* Horizontal scroll zone enabled */
+
+ unsigned char last_reg; /* Last register we requested read from */
+ unsigned char last_val;
+};
+
+#ifdef CONFIG_MOUSE_PS2_SENTELIC
+extern int fsp_detect(struct psmouse *psmouse, bool set_properties);
+extern int fsp_init(struct psmouse *psmouse);
+#else
+inline int fsp_detect(struct psmouse *psmouse, bool set_properties)
+{
+ return -ENOSYS;
+}
+inline int fsp_init(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* !__SENTELIC_H */
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 19984bf06ca..b66ff1ac7de 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -60,7 +60,7 @@ static int synaptics_mode_cmd(struct psmouse *psmouse, unsigned char mode)
return 0;
}
-int synaptics_detect(struct psmouse *psmouse, int set_properties)
+int synaptics_detect(struct psmouse *psmouse, bool set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
@@ -556,38 +556,38 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
{
int i;
- set_bit(EV_ABS, dev->evbit);
+ __set_bit(EV_ABS, dev->evbit);
input_set_abs_params(dev, ABS_X, XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
- set_bit(ABS_TOOL_WIDTH, dev->absbit);
+ __set_bit(ABS_TOOL_WIDTH, dev->absbit);
- set_bit(EV_KEY, dev->evbit);
- set_bit(BTN_TOUCH, dev->keybit);
- set_bit(BTN_TOOL_FINGER, dev->keybit);
- set_bit(BTN_LEFT, dev->keybit);
- set_bit(BTN_RIGHT, dev->keybit);
+ __set_bit(EV_KEY, dev->evbit);
+ __set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOOL_FINGER, dev->keybit);
+ __set_bit(BTN_LEFT, dev->keybit);
+ __set_bit(BTN_RIGHT, dev->keybit);
if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
- set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
- set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
}
if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
- set_bit(BTN_MIDDLE, dev->keybit);
+ __set_bit(BTN_MIDDLE, dev->keybit);
if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
- set_bit(BTN_FORWARD, dev->keybit);
- set_bit(BTN_BACK, dev->keybit);
+ __set_bit(BTN_FORWARD, dev->keybit);
+ __set_bit(BTN_BACK, dev->keybit);
}
for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
- set_bit(BTN_0 + i, dev->keybit);
+ __set_bit(BTN_0 + i, dev->keybit);
- clear_bit(EV_REL, dev->evbit);
- clear_bit(REL_X, dev->relbit);
- clear_bit(REL_Y, dev->relbit);
+ __clear_bit(EV_REL, dev->evbit);
+ __clear_bit(REL_X, dev->relbit);
+ __clear_bit(REL_Y, dev->relbit);
dev->absres[ABS_X] = priv->x_res;
dev->absres[ABS_Y] = priv->y_res;
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h
index 30238215175..871f6fe377f 100644
--- a/drivers/input/mouse/synaptics.h
+++ b/drivers/input/mouse/synaptics.h
@@ -105,7 +105,7 @@ struct synaptics_data {
int scroll;
};
-int synaptics_detect(struct psmouse *psmouse, int set_properties);
+int synaptics_detect(struct psmouse *psmouse, bool set_properties);
int synaptics_init(struct psmouse *psmouse);
void synaptics_reset(struct psmouse *psmouse);
diff --git a/drivers/input/mouse/touchkit_ps2.c b/drivers/input/mouse/touchkit_ps2.c
index 3fadb2accac..0308a0faa94 100644
--- a/drivers/input/mouse/touchkit_ps2.c
+++ b/drivers/input/mouse/touchkit_ps2.c
@@ -67,7 +67,7 @@ static psmouse_ret_t touchkit_ps2_process_byte(struct psmouse *psmouse)
return PSMOUSE_FULL_PACKET;
}
-int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties)
+int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties)
{
struct input_dev *dev = psmouse->dev;
unsigned char param[3];
@@ -86,7 +86,7 @@ int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties)
if (set_properties) {
dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
- set_bit(BTN_TOUCH, dev->keybit);
+ __set_bit(BTN_TOUCH, dev->keybit);
input_set_abs_params(dev, ABS_X, 0, TOUCHKIT_MAX_XC, 0, 0);
input_set_abs_params(dev, ABS_Y, 0, TOUCHKIT_MAX_YC, 0, 0);
diff --git a/drivers/input/mouse/touchkit_ps2.h b/drivers/input/mouse/touchkit_ps2.h
index 8a0dd3574ae..2efe9ea29d0 100644
--- a/drivers/input/mouse/touchkit_ps2.h
+++ b/drivers/input/mouse/touchkit_ps2.h
@@ -13,10 +13,10 @@
#define _TOUCHKIT_PS2_H
#ifdef CONFIG_MOUSE_PS2_TOUCHKIT
-int touchkit_ps2_detect(struct psmouse *psmouse, int set_properties);
+int touchkit_ps2_detect(struct psmouse *psmouse, bool set_properties);
#else
static inline int touchkit_ps2_detect(struct psmouse *psmouse,
- int set_properties)
+ bool set_properties)
{
return -ENOSYS;
}
diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c
index e68c814c436..e354362f297 100644
--- a/drivers/input/mouse/trackpoint.c
+++ b/drivers/input/mouse/trackpoint.c
@@ -282,7 +282,7 @@ static int trackpoint_reconnect(struct psmouse *psmouse)
return 0;
}
-int trackpoint_detect(struct psmouse *psmouse, int set_properties)
+int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
{
struct trackpoint_data *priv;
struct ps2dev *ps2dev = &psmouse->ps2dev;
diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h
index c10a6e7d010..e558a709661 100644
--- a/drivers/input/mouse/trackpoint.h
+++ b/drivers/input/mouse/trackpoint.h
@@ -143,9 +143,9 @@ struct trackpoint_data
};
#ifdef CONFIG_MOUSE_PS2_TRACKPOINT
-int trackpoint_detect(struct psmouse *psmouse, int set_properties);
+int trackpoint_detect(struct psmouse *psmouse, bool set_properties);
#else
-inline int trackpoint_detect(struct psmouse *psmouse, int set_properties)
+inline int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
{
return -ENOSYS;
}
diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c
index 404eedd5ffa..70111443678 100644
--- a/drivers/input/mouse/vsxxxaa.c
+++ b/drivers/input/mouse/vsxxxaa.c
@@ -384,11 +384,11 @@ vsxxxaa_handle_POR_packet (struct vsxxxaa *mouse)
printk (KERN_NOTICE "%s on %s: Forceing standard packet format, "
"incremental streaming mode and 72 samples/sec\n",
mouse->name, mouse->phys);
- mouse->serio->write (mouse->serio, 'S'); /* Standard format */
+ serio_write (mouse->serio, 'S'); /* Standard format */
mdelay (50);
- mouse->serio->write (mouse->serio, 'R'); /* Incremental */
+ serio_write (mouse->serio, 'R'); /* Incremental */
mdelay (50);
- mouse->serio->write (mouse->serio, 'L'); /* 72 samples/sec */
+ serio_write (mouse->serio, 'L'); /* 72 samples/sec */
}
static void
@@ -532,7 +532,7 @@ vsxxxaa_connect (struct serio *serio, struct serio_driver *drv)
* Request selftest. Standard packet format and differential
* mode will be requested after the device ID'ed successfully.
*/
- serio->write (serio, 'T'); /* Test */
+ serio_write (serio, 'T'); /* Test */
err = input_register_device (input_dev);
if (err)
diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c
index 41fda8c67b1..a6fb7a3dcc4 100644
--- a/drivers/input/serio/at32psif.c
+++ b/drivers/input/serio/at32psif.c
@@ -231,7 +231,7 @@ static int __init psif_probe(struct platform_device *pdev)
goto out_free_io;
}
- psif->regs = ioremap(regs->start, regs->end - regs->start + 1);
+ psif->regs = ioremap(regs->start, resource_size(regs));
if (!psif->regs) {
ret = -ENOMEM;
dev_dbg(&pdev->dev, "could not map I/O memory\n");
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index ccbf23ece8e..a39bc4eb902 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -457,6 +457,34 @@ static struct dmi_system_id __initdata i8042_dmi_nopnp_table[] = {
},
{ }
};
+
+static struct dmi_system_id __initdata i8042_dmi_laptop_table[] = {
+ {
+ .ident = "Portable",
+ .matches = {
+ DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
+ },
+ },
+ {
+ .ident = "Laptop",
+ .matches = {
+ DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */
+ },
+ },
+ {
+ .ident = "Notebook",
+ .matches = {
+ DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
+ },
+ },
+ {
+ .ident = "Sub-Notebook",
+ .matches = {
+ DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
+ },
+ },
+ { }
+};
#endif
/*
@@ -530,9 +558,9 @@ static struct dmi_system_id __initdata i8042_dmi_dritek_table[] = {
#ifdef CONFIG_PNP
#include <linux/pnp.h>
-static int i8042_pnp_kbd_registered;
+static bool i8042_pnp_kbd_registered;
static unsigned int i8042_pnp_kbd_devices;
-static int i8042_pnp_aux_registered;
+static bool i8042_pnp_aux_registered;
static unsigned int i8042_pnp_aux_devices;
static int i8042_pnp_command_reg;
@@ -620,12 +648,12 @@ static struct pnp_driver i8042_pnp_aux_driver = {
static void i8042_pnp_exit(void)
{
if (i8042_pnp_kbd_registered) {
- i8042_pnp_kbd_registered = 0;
+ i8042_pnp_kbd_registered = false;
pnp_unregister_driver(&i8042_pnp_kbd_driver);
}
if (i8042_pnp_aux_registered) {
- i8042_pnp_aux_registered = 0;
+ i8042_pnp_aux_registered = false;
pnp_unregister_driver(&i8042_pnp_aux_driver);
}
}
@@ -633,12 +661,12 @@ static void i8042_pnp_exit(void)
static int __init i8042_pnp_init(void)
{
char kbd_irq_str[4] = { 0 }, aux_irq_str[4] = { 0 };
- int pnp_data_busted = 0;
+ int pnp_data_busted = false;
int err;
#ifdef CONFIG_X86
if (dmi_check_system(i8042_dmi_nopnp_table))
- i8042_nopnp = 1;
+ i8042_nopnp = true;
#endif
if (i8042_nopnp) {
@@ -648,11 +676,11 @@ static int __init i8042_pnp_init(void)
err = pnp_register_driver(&i8042_pnp_kbd_driver);
if (!err)
- i8042_pnp_kbd_registered = 1;
+ i8042_pnp_kbd_registered = true;
err = pnp_register_driver(&i8042_pnp_aux_driver);
if (!err)
- i8042_pnp_aux_registered = 1;
+ i8042_pnp_aux_registered = true;
if (!i8042_pnp_kbd_devices && !i8042_pnp_aux_devices) {
i8042_pnp_exit();
@@ -680,9 +708,9 @@ static int __init i8042_pnp_init(void)
#if defined(__ia64__)
if (!i8042_pnp_kbd_devices)
- i8042_nokbd = 1;
+ i8042_nokbd = true;
if (!i8042_pnp_aux_devices)
- i8042_noaux = 1;
+ i8042_noaux = true;
#endif
if (((i8042_pnp_data_reg & ~0xf) == (i8042_data_reg & ~0xf) &&
@@ -693,7 +721,7 @@ static int __init i8042_pnp_init(void)
"using default %#x\n",
i8042_pnp_data_reg, i8042_data_reg);
i8042_pnp_data_reg = i8042_data_reg;
- pnp_data_busted = 1;
+ pnp_data_busted = true;
}
if (((i8042_pnp_command_reg & ~0xf) == (i8042_command_reg & ~0xf) &&
@@ -704,7 +732,7 @@ static int __init i8042_pnp_init(void)
"using default %#x\n",
i8042_pnp_command_reg, i8042_command_reg);
i8042_pnp_command_reg = i8042_command_reg;
- pnp_data_busted = 1;
+ pnp_data_busted = true;
}
if (!i8042_nokbd && !i8042_pnp_kbd_irq) {
@@ -712,7 +740,7 @@ static int __init i8042_pnp_init(void)
"PNP: PS/2 controller doesn't have KBD irq; "
"using default %d\n", i8042_kbd_irq);
i8042_pnp_kbd_irq = i8042_kbd_irq;
- pnp_data_busted = 1;
+ pnp_data_busted = true;
}
if (!i8042_noaux && !i8042_pnp_aux_irq) {
@@ -721,7 +749,7 @@ static int __init i8042_pnp_init(void)
"PNP: PS/2 appears to have AUX port disabled, "
"if this is incorrect please boot with "
"i8042.nopnp\n");
- i8042_noaux = 1;
+ i8042_noaux = true;
} else {
printk(KERN_WARNING
"PNP: PS/2 controller doesn't have AUX irq; "
@@ -735,6 +763,11 @@ static int __init i8042_pnp_init(void)
i8042_kbd_irq = i8042_pnp_kbd_irq;
i8042_aux_irq = i8042_pnp_aux_irq;
+#ifdef CONFIG_X86
+ i8042_bypass_aux_irq_test = !pnp_data_busted &&
+ dmi_check_system(i8042_dmi_laptop_table);
+#endif
+
return 0;
}
@@ -763,21 +796,21 @@ static int __init i8042_platform_init(void)
return retval;
#if defined(__ia64__)
- i8042_reset = 1;
+ i8042_reset = true;
#endif
#ifdef CONFIG_X86
if (dmi_check_system(i8042_dmi_reset_table))
- i8042_reset = 1;
+ i8042_reset = true;
if (dmi_check_system(i8042_dmi_noloop_table))
- i8042_noloop = 1;
+ i8042_noloop = true;
if (dmi_check_system(i8042_dmi_nomux_table))
- i8042_nomux = 1;
+ i8042_nomux = true;
if (dmi_check_system(i8042_dmi_dritek_table))
- i8042_dritek = 1;
+ i8042_dritek = true;
#endif /* CONFIG_X86 */
return retval;
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 582245c497e..eb3ff94af58 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -28,35 +28,35 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver");
MODULE_LICENSE("GPL");
-static unsigned int i8042_nokbd;
+static bool i8042_nokbd;
module_param_named(nokbd, i8042_nokbd, bool, 0);
MODULE_PARM_DESC(nokbd, "Do not probe or use KBD port.");
-static unsigned int i8042_noaux;
+static bool i8042_noaux;
module_param_named(noaux, i8042_noaux, bool, 0);
MODULE_PARM_DESC(noaux, "Do not probe or use AUX (mouse) port.");
-static unsigned int i8042_nomux;
+static bool i8042_nomux;
module_param_named(nomux, i8042_nomux, bool, 0);
MODULE_PARM_DESC(nomux, "Do not check whether an active multiplexing conrtoller is present.");
-static unsigned int i8042_unlock;
+static bool i8042_unlock;
module_param_named(unlock, i8042_unlock, bool, 0);
MODULE_PARM_DESC(unlock, "Ignore keyboard lock.");
-static unsigned int i8042_reset;
+static bool i8042_reset;
module_param_named(reset, i8042_reset, bool, 0);
MODULE_PARM_DESC(reset, "Reset controller during init and cleanup.");
-static unsigned int i8042_direct;
+static bool i8042_direct;
module_param_named(direct, i8042_direct, bool, 0);
MODULE_PARM_DESC(direct, "Put keyboard port into non-translated mode.");
-static unsigned int i8042_dumbkbd;
+static bool i8042_dumbkbd;
module_param_named(dumbkbd, i8042_dumbkbd, bool, 0);
MODULE_PARM_DESC(dumbkbd, "Pretend that controller can only read data from keyboard");
-static unsigned int i8042_noloop;
+static bool i8042_noloop;
module_param_named(noloop, i8042_noloop, bool, 0);
MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port");
@@ -65,24 +65,26 @@ module_param_named(panicblink, i8042_blink_frequency, uint, 0600);
MODULE_PARM_DESC(panicblink, "Frequency with which keyboard LEDs should blink when kernel panics");
#ifdef CONFIG_X86
-static unsigned int i8042_dritek;
+static bool i8042_dritek;
module_param_named(dritek, i8042_dritek, bool, 0);
MODULE_PARM_DESC(dritek, "Force enable the Dritek keyboard extension");
#endif
#ifdef CONFIG_PNP
-static int i8042_nopnp;
+static bool i8042_nopnp;
module_param_named(nopnp, i8042_nopnp, bool, 0);
MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings");
#endif
#define DEBUG
#ifdef DEBUG
-static int i8042_debug;
+static bool i8042_debug;
module_param_named(debug, i8042_debug, bool, 0600);
MODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off");
#endif
+static bool i8042_bypass_aux_irq_test;
+
#include "i8042.h"
static DEFINE_SPINLOCK(i8042_lock);
@@ -90,7 +92,7 @@ static DEFINE_SPINLOCK(i8042_lock);
struct i8042_port {
struct serio *serio;
int irq;
- unsigned char exists;
+ bool exists;
signed char mux;
};
@@ -103,9 +105,9 @@ static struct i8042_port i8042_ports[I8042_NUM_PORTS];
static unsigned char i8042_initial_ctr;
static unsigned char i8042_ctr;
-static unsigned char i8042_mux_present;
-static unsigned char i8042_kbd_irq_registered;
-static unsigned char i8042_aux_irq_registered;
+static bool i8042_mux_present;
+static bool i8042_kbd_irq_registered;
+static bool i8042_aux_irq_registered;
static unsigned char i8042_suppress_kbd_ack;
static struct platform_device *i8042_platform_device;
@@ -262,6 +264,49 @@ static int i8042_aux_write(struct serio *serio, unsigned char c)
I8042_CMD_MUX_SEND + port->mux);
}
+
+/*
+ * i8042_aux_close attempts to clear AUX or KBD port state by disabling
+ * and then re-enabling it.
+ */
+
+static void i8042_port_close(struct serio *serio)
+{
+ int irq_bit;
+ int disable_bit;
+ const char *port_name;
+
+ if (serio == i8042_ports[I8042_AUX_PORT_NO].serio) {
+ irq_bit = I8042_CTR_AUXINT;
+ disable_bit = I8042_CTR_AUXDIS;
+ port_name = "AUX";
+ } else {
+ irq_bit = I8042_CTR_KBDINT;
+ disable_bit = I8042_CTR_KBDDIS;
+ port_name = "KBD";
+ }
+
+ i8042_ctr &= ~irq_bit;
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+ printk(KERN_WARNING
+ "i8042.c: Can't write CTR while closing %s port.\n",
+ port_name);
+
+ udelay(50);
+
+ i8042_ctr &= ~disable_bit;
+ i8042_ctr |= irq_bit;
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+ printk(KERN_ERR "i8042.c: Can't reactivate %s port.\n",
+ port_name);
+
+ /*
+ * See if there is any data appeared while we were messing with
+ * port state.
+ */
+ i8042_interrupt(0, NULL);
+}
+
/*
* i8042_start() is called by serio core when port is about to finish
* registering. It will mark port as existing so i8042_interrupt can
@@ -271,7 +316,7 @@ static int i8042_start(struct serio *serio)
{
struct i8042_port *port = serio->port_data;
- port->exists = 1;
+ port->exists = true;
mb();
return 0;
}
@@ -285,7 +330,7 @@ static void i8042_stop(struct serio *serio)
{
struct i8042_port *port = serio->port_data;
- port->exists = 0;
+ port->exists = false;
/*
* We synchronize with both AUX and KBD IRQs because there is
@@ -391,7 +436,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
}
/*
- * i8042_enable_kbd_port enables keybaord port on chip
+ * i8042_enable_kbd_port enables keyboard port on chip
*/
static int i8042_enable_kbd_port(void)
@@ -447,14 +492,15 @@ static int i8042_enable_mux_ports(void)
}
/*
- * i8042_set_mux_mode checks whether the controller has an active
- * multiplexor and puts the chip into Multiplexed (1) or Legacy (0) mode.
+ * i8042_set_mux_mode checks whether the controller has an
+ * active multiplexor and puts the chip into Multiplexed (true)
+ * or Legacy (false) mode.
*/
-static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version)
+static int i8042_set_mux_mode(bool multiplex, unsigned char *mux_version)
{
- unsigned char param;
+ unsigned char param, val;
/*
* Get rid of bytes in the queue.
*/
@@ -466,14 +512,21 @@ static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version)
* mouse interface, the last should be version.
*/
- param = 0xf0;
- if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0xf0)
+ param = val = 0xf0;
+ if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != val)
return -1;
- param = mode ? 0x56 : 0xf6;
- if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != (mode ? 0x56 : 0xf6))
+ param = val = multiplex ? 0x56 : 0xf6;
+ if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != val)
return -1;
- param = mode ? 0xa4 : 0xa5;
- if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param == (mode ? 0xa4 : 0xa5))
+ param = val = multiplex ? 0xa4 : 0xa5;
+ if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param == val)
+ return -1;
+
+/*
+ * Workaround for interference with USB Legacy emulation
+ * that causes a v10.12 MUX to be found.
+ */
+ if (param == 0xac)
return -1;
if (mux_version)
@@ -488,18 +541,11 @@ static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version)
* LCS/Telegraphics.
*/
-static int __devinit i8042_check_mux(void)
+static int __init i8042_check_mux(void)
{
unsigned char mux_version;
- if (i8042_set_mux_mode(1, &mux_version))
- return -1;
-
-/*
- * Workaround for interference with USB Legacy emulation
- * that causes a v10.12 MUX to be found.
- */
- if (mux_version == 0xAC)
+ if (i8042_set_mux_mode(true, &mux_version))
return -1;
printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n",
@@ -516,7 +562,7 @@ static int __devinit i8042_check_mux(void)
return -EIO;
}
- i8042_mux_present = 1;
+ i8042_mux_present = true;
return 0;
}
@@ -524,10 +570,10 @@ static int __devinit i8042_check_mux(void)
/*
* The following is used to test AUX IRQ delivery.
*/
-static struct completion i8042_aux_irq_delivered __devinitdata;
-static int i8042_irq_being_tested __devinitdata;
+static struct completion i8042_aux_irq_delivered __initdata;
+static bool i8042_irq_being_tested __initdata;
-static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id)
+static irqreturn_t __init i8042_aux_test_irq(int irq, void *dev_id)
{
unsigned long flags;
unsigned char str, data;
@@ -552,7 +598,7 @@ static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id)
* verifies success by readinng CTR. Used when testing for presence of AUX
* port.
*/
-static int __devinit i8042_toggle_aux(int on)
+static int __init i8042_toggle_aux(bool on)
{
unsigned char param;
int i;
@@ -580,11 +626,11 @@ static int __devinit i8042_toggle_aux(int on)
* the presence of an AUX interface.
*/
-static int __devinit i8042_check_aux(void)
+static int __init i8042_check_aux(void)
{
int retval = -1;
- int irq_registered = 0;
- int aux_loop_broken = 0;
+ bool irq_registered = false;
+ bool aux_loop_broken = false;
unsigned long flags;
unsigned char param;
@@ -621,19 +667,19 @@ static int __devinit i8042_check_aux(void)
* mark it as broken
*/
if (!retval)
- aux_loop_broken = 1;
+ aux_loop_broken = true;
}
/*
* Bit assignment test - filters out PS/2 i8042's in AT mode
*/
- if (i8042_toggle_aux(0)) {
+ if (i8042_toggle_aux(false)) {
printk(KERN_WARNING "Failed to disable AUX port, but continuing anyway... Is this a SiS?\n");
printk(KERN_WARNING "If AUX port is really absent please use the 'i8042.noaux' option.\n");
}
- if (i8042_toggle_aux(1))
+ if (i8042_toggle_aux(true))
return -1;
/*
@@ -641,7 +687,7 @@ static int __devinit i8042_check_aux(void)
* used it for a PCI card or somethig else.
*/
- if (i8042_noloop || aux_loop_broken) {
+ if (i8042_noloop || i8042_bypass_aux_irq_test || aux_loop_broken) {
/*
* Without LOOP command we can't test AUX IRQ delivery. Assume the port
* is working and hope we are right.
@@ -654,7 +700,7 @@ static int __devinit i8042_check_aux(void)
"i8042", i8042_platform_device))
goto out;
- irq_registered = 1;
+ irq_registered = true;
if (i8042_enable_aux_port())
goto out;
@@ -662,7 +708,7 @@ static int __devinit i8042_check_aux(void)
spin_lock_irqsave(&i8042_lock, flags);
init_completion(&i8042_aux_irq_delivered);
- i8042_irq_being_tested = 1;
+ i8042_irq_being_tested = true;
param = 0xa5;
retval = __i8042_command(&param, I8042_CMD_AUX_LOOP & 0xf0ff);
@@ -799,7 +845,7 @@ static int i8042_controller_init(void)
*/
if (~i8042_ctr & I8042_CTR_XLATE)
- i8042_direct = 1;
+ i8042_direct = true;
/*
* Set nontranslated mode for the kbd interface if requested by an option.
@@ -839,12 +885,15 @@ static void i8042_controller_reset(void)
i8042_ctr |= I8042_CTR_KBDDIS | I8042_CTR_AUXDIS;
i8042_ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT);
+ if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR))
+ printk(KERN_WARNING "i8042.c: Can't write CTR while resetting.\n");
+
/*
* Disable MUX mode if present.
*/
if (i8042_mux_present)
- i8042_set_mux_mode(0, NULL);
+ i8042_set_mux_mode(false, NULL);
/*
* Reset the controller if requested.
@@ -923,41 +972,27 @@ static void i8042_dritek_enable(void)
#ifdef CONFIG_PM
-static bool i8042_suspended;
-
/*
- * Here we try to restore the original BIOS settings. We only want to
- * do that once, when we really suspend, not when we taking memory
- * snapshot for swsusp (in this case we'll perform required cleanup
- * as part of shutdown process).
+ * Here we try to restore the original BIOS settings to avoid
+ * upsetting it.
*/
-static int i8042_suspend(struct platform_device *dev, pm_message_t state)
+static int i8042_pm_reset(struct device *dev)
{
- if (!i8042_suspended && state.event == PM_EVENT_SUSPEND)
- i8042_controller_reset();
-
- i8042_suspended = state.event == PM_EVENT_SUSPEND ||
- state.event == PM_EVENT_FREEZE;
+ i8042_controller_reset();
return 0;
}
-
/*
- * Here we try to reset everything back to a state in which suspended
+ * Here we try to reset everything back to a state we had
+ * before suspending.
*/
-static int i8042_resume(struct platform_device *dev)
+static int i8042_pm_restore(struct device *dev)
{
int error;
-/*
- * Do not bother with restoring state if we haven't suspened yet
- */
- if (!i8042_suspended)
- return 0;
-
error = i8042_controller_check();
if (error)
return error;
@@ -991,7 +1026,7 @@ static int i8042_resume(struct platform_device *dev)
#endif
if (i8042_mux_present) {
- if (i8042_set_mux_mode(1, NULL) || i8042_enable_mux_ports())
+ if (i8042_set_mux_mode(true, NULL) || i8042_enable_mux_ports())
printk(KERN_WARNING
"i8042: failed to resume active multiplexor, "
"mouse won't work.\n");
@@ -1001,11 +1036,18 @@ static int i8042_resume(struct platform_device *dev)
if (i8042_ports[I8042_KBD_PORT_NO].serio)
i8042_enable_kbd_port();
- i8042_suspended = false;
i8042_interrupt(0, NULL);
return 0;
}
+
+static const struct dev_pm_ops i8042_pm_ops = {
+ .suspend = i8042_pm_reset,
+ .resume = i8042_pm_restore,
+ .poweroff = i8042_pm_reset,
+ .restore = i8042_pm_restore,
+};
+
#endif /* CONFIG_PM */
/*
@@ -1018,7 +1060,7 @@ static void i8042_shutdown(struct platform_device *dev)
i8042_controller_reset();
}
-static int __devinit i8042_create_kbd_port(void)
+static int __init i8042_create_kbd_port(void)
{
struct serio *serio;
struct i8042_port *port = &i8042_ports[I8042_KBD_PORT_NO];
@@ -1031,6 +1073,7 @@ static int __devinit i8042_create_kbd_port(void)
serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write;
serio->start = i8042_start;
serio->stop = i8042_stop;
+ serio->close = i8042_port_close;
serio->port_data = port;
serio->dev.parent = &i8042_platform_device->dev;
strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name));
@@ -1042,7 +1085,7 @@ static int __devinit i8042_create_kbd_port(void)
return 0;
}
-static int __devinit i8042_create_aux_port(int idx)
+static int __init i8042_create_aux_port(int idx)
{
struct serio *serio;
int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx;
@@ -1061,6 +1104,7 @@ static int __devinit i8042_create_aux_port(int idx)
if (idx < 0) {
strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name));
strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
+ serio->close = i8042_port_close;
} else {
snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx);
snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1);
@@ -1073,13 +1117,13 @@ static int __devinit i8042_create_aux_port(int idx)
return 0;
}
-static void __devinit i8042_free_kbd_port(void)
+static void __init i8042_free_kbd_port(void)
{
kfree(i8042_ports[I8042_KBD_PORT_NO].serio);
i8042_ports[I8042_KBD_PORT_NO].serio = NULL;
}
-static void __devinit i8042_free_aux_ports(void)
+static void __init i8042_free_aux_ports(void)
{
int i;
@@ -1089,7 +1133,7 @@ static void __devinit i8042_free_aux_ports(void)
}
}
-static void __devinit i8042_register_ports(void)
+static void __init i8042_register_ports(void)
{
int i;
@@ -1124,10 +1168,10 @@ static void i8042_free_irqs(void)
if (i8042_kbd_irq_registered)
free_irq(I8042_KBD_IRQ, i8042_platform_device);
- i8042_aux_irq_registered = i8042_kbd_irq_registered = 0;
+ i8042_aux_irq_registered = i8042_kbd_irq_registered = false;
}
-static int __devinit i8042_setup_aux(void)
+static int __init i8042_setup_aux(void)
{
int (*aux_enable)(void);
int error;
@@ -1158,7 +1202,7 @@ static int __devinit i8042_setup_aux(void)
if (aux_enable())
goto err_free_irq;
- i8042_aux_irq_registered = 1;
+ i8042_aux_irq_registered = true;
return 0;
err_free_irq:
@@ -1168,7 +1212,7 @@ static int __devinit i8042_setup_aux(void)
return error;
}
-static int __devinit i8042_setup_kbd(void)
+static int __init i8042_setup_kbd(void)
{
int error;
@@ -1185,7 +1229,7 @@ static int __devinit i8042_setup_kbd(void)
if (error)
goto err_free_irq;
- i8042_kbd_irq_registered = 1;
+ i8042_kbd_irq_registered = true;
return 0;
err_free_irq:
@@ -1195,7 +1239,7 @@ static int __devinit i8042_setup_kbd(void)
return error;
}
-static int __devinit i8042_probe(struct platform_device *dev)
+static int __init i8042_probe(struct platform_device *dev)
{
int error;
@@ -1251,14 +1295,12 @@ static struct platform_driver i8042_driver = {
.driver = {
.name = "i8042",
.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &i8042_pm_ops,
+#endif
},
- .probe = i8042_probe,
.remove = __devexit_p(i8042_remove),
.shutdown = i8042_shutdown,
-#ifdef CONFIG_PM
- .suspend = i8042_suspend,
- .resume = i8042_resume,
-#endif
};
static int __init i8042_init(void)
@@ -1275,28 +1317,28 @@ static int __init i8042_init(void)
if (err)
goto err_platform_exit;
- err = platform_driver_register(&i8042_driver);
- if (err)
- goto err_platform_exit;
-
i8042_platform_device = platform_device_alloc("i8042", -1);
if (!i8042_platform_device) {
err = -ENOMEM;
- goto err_unregister_driver;
+ goto err_platform_exit;
}
err = platform_device_add(i8042_platform_device);
if (err)
goto err_free_device;
+ err = platform_driver_probe(&i8042_driver, i8042_probe);
+ if (err)
+ goto err_del_device;
+
panic_blink = i8042_panic_blink;
return 0;
+ err_del_device:
+ platform_device_del(i8042_platform_device);
err_free_device:
platform_device_put(i8042_platform_device);
- err_unregister_driver:
- platform_driver_unregister(&i8042_driver);
err_platform_exit:
i8042_platform_exit();
@@ -1305,8 +1347,8 @@ static int __init i8042_init(void)
static void __exit i8042_exit(void)
{
- platform_device_unregister(i8042_platform_device);
platform_driver_unregister(&i8042_driver);
+ platform_device_unregister(i8042_platform_device);
i8042_platform_exit();
panic_blink = NULL;
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index be5bbbb8ae4..3a95b508bf2 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -161,7 +161,7 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
* ps2_command() can only be called from a process context
*/
-int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
+int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
{
int timeout;
int send = (command >> 12) & 0xf;
@@ -179,8 +179,6 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
return -1;
}
- mutex_lock(&ps2dev->cmd_mutex);
-
serio_pause_rx(ps2dev->serio);
ps2dev->flags = command == PS2_CMD_GETID ? PS2_FLAG_WAITID : 0;
ps2dev->cmdcnt = receive;
@@ -231,7 +229,18 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
ps2dev->flags = 0;
serio_continue_rx(ps2dev->serio);
+ return rc;
+}
+EXPORT_SYMBOL(__ps2_command);
+
+int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
+{
+ int rc;
+
+ mutex_lock(&ps2dev->cmd_mutex);
+ rc = __ps2_command(ps2dev, param, command);
mutex_unlock(&ps2dev->cmd_mutex);
+
return rc;
}
EXPORT_SYMBOL(ps2_command);
diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c
index d66f4944f2a..0236f0d5fd9 100644
--- a/drivers/input/serio/serio.c
+++ b/drivers/input/serio/serio.c
@@ -931,15 +931,11 @@ static int serio_uevent(struct device *dev, struct kobj_uevent_env *env)
#endif /* CONFIG_HOTPLUG */
#ifdef CONFIG_PM
-static int serio_suspend(struct device *dev, pm_message_t state)
+static int serio_suspend(struct device *dev)
{
struct serio *serio = to_serio_port(dev);
- if (!serio->suspended && state.event == PM_EVENT_SUSPEND)
- serio_cleanup(serio);
-
- serio->suspended = state.event == PM_EVENT_SUSPEND ||
- state.event == PM_EVENT_FREEZE;
+ serio_cleanup(serio);
return 0;
}
@@ -952,13 +948,17 @@ static int serio_resume(struct device *dev)
* Driver reconnect can take a while, so better let kseriod
* deal with it.
*/
- if (serio->suspended) {
- serio->suspended = false;
- serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
- }
+ serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
return 0;
}
+
+static const struct dev_pm_ops serio_pm_ops = {
+ .suspend = serio_suspend,
+ .resume = serio_resume,
+ .poweroff = serio_suspend,
+ .restore = serio_resume,
+};
#endif /* CONFIG_PM */
/* called from serio_driver->connect/disconnect methods under serio_mutex */
@@ -1015,8 +1015,7 @@ static struct bus_type serio_bus = {
.remove = serio_driver_remove,
.shutdown = serio_shutdown,
#ifdef CONFIG_PM
- .suspend = serio_suspend,
- .resume = serio_resume,
+ .pm = &serio_pm_ops,
#endif
};
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 72e2712c7e2..87a1ae63bcc 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -366,11 +366,11 @@ config TOUCHSCREEN_WM97XX_ATMEL
be called atmel-wm97xx.
config TOUCHSCREEN_WM97XX_MAINSTONE
- tristate "WM97xx Mainstone accelerated touch"
+ tristate "WM97xx Mainstone/Palm accelerated touch"
depends on TOUCHSCREEN_WM97XX && ARCH_PXA
help
Say Y here for support for streaming mode with WM97xx touchscreens
- on Mainstone systems.
+ on Mainstone, Palm Tungsten T5, TX and LifeDrive systems.
If unsure, say N.
@@ -406,6 +406,7 @@ config TOUCHSCREEN_USB_COMPOSITE
- IRTOUCHSYSTEMS/UNITOP
- IdealTEK URTC1000
- GoTop Super_Q2/GogoPen/PenPower tablets
+ - JASTEC USB Touch Controller/DigiTech DTR-02U
Have a look at <http://linux.chapter7.ch/touchkit/> for
a usage description and the required user-space stuff.
@@ -468,6 +469,16 @@ config TOUCHSCREEN_USB_GOTOP
bool "GoTop Super_Q2/GogoPen/PenPower tablet device support" if EMBEDDED
depends on TOUCHSCREEN_USB_COMPOSITE
+config TOUCHSCREEN_USB_JASTEC
+ default y
+ bool "JASTEC/DigiTech DTR-02U USB touch controller device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
+config TOUCHSCREEN_USB_E2I
+ default y
+ bool "e2i Touchscreen controller (e.g. from Mimo 740)"
+ depends on TOUCHSCREEN_USB_COMPOSITE
+
config TOUCHSCREEN_TOUCHIT213
tristate "Sahara TouchIT-213 touchscreen"
select SERIO
@@ -492,6 +503,7 @@ config TOUCHSCREEN_TSC2007
config TOUCHSCREEN_W90X900
tristate "W90P910 touchscreen driver"
+ depends on HAVE_CLK
help
Say Y here if you have a W90P910 based touchscreen.
diff --git a/drivers/input/touchscreen/atmel_tsadcc.c b/drivers/input/touchscreen/atmel_tsadcc.c
index 055969e8be1..9c7fce4d74d 100644
--- a/drivers/input/touchscreen/atmel_tsadcc.c
+++ b/drivers/input/touchscreen/atmel_tsadcc.c
@@ -204,14 +204,14 @@ static int __devinit atmel_tsadcc_probe(struct platform_device *pdev)
goto err_free_dev;
}
- if (!request_mem_region(res->start, res->end - res->start + 1,
+ if (!request_mem_region(res->start, resource_size(res),
"atmel tsadcc regs")) {
dev_err(&pdev->dev, "resources is unavailable.\n");
err = -EBUSY;
goto err_free_dev;
}
- tsc_base = ioremap(res->start, res->end - res->start + 1);
+ tsc_base = ioremap(res->start, resource_size(res));
if (!tsc_base) {
dev_err(&pdev->dev, "failed to map registers.\n");
err = -ENOMEM;
@@ -286,7 +286,7 @@ err_free_irq:
err_unmap_regs:
iounmap(tsc_base);
err_release_mem:
- release_mem_region(res->start, res->end - res->start + 1);
+ release_mem_region(res->start, resource_size(res));
err_free_dev:
input_free_device(ts_dev->input);
err_free_mem:
@@ -305,7 +305,7 @@ static int __devexit atmel_tsadcc_remove(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iounmap(tsc_base);
- release_mem_region(res->start, res->end - res->start + 1);
+ release_mem_region(res->start, resource_size(res));
clk_disable(ts_dev->clk);
clk_put(ts_dev->clk);
diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c
index 3ab92222a52..9029bd3f34e 100644
--- a/drivers/input/touchscreen/eeti_ts.c
+++ b/drivers/input/touchscreen/eeti_ts.c
@@ -32,6 +32,7 @@
#include <linux/i2c.h>
#include <linux/timer.h>
#include <linux/gpio.h>
+#include <linux/input/eeti_ts.h>
static int flip_x;
module_param(flip_x, bool, 0644);
@@ -46,7 +47,7 @@ struct eeti_ts_priv {
struct input_dev *input;
struct work_struct work;
struct mutex mutex;
- int irq;
+ int irq, irq_active_high;
};
#define EETI_TS_BITDEPTH (11)
@@ -58,6 +59,11 @@ struct eeti_ts_priv {
#define REPORT_BIT_HAS_PRESSURE (1 << 6)
#define REPORT_RES_BITS(v) (((v) >> 1) + EETI_TS_BITDEPTH)
+static inline int eeti_ts_irq_active(struct eeti_ts_priv *priv)
+{
+ return gpio_get_value(irq_to_gpio(priv->irq)) == priv->irq_active_high;
+}
+
static void eeti_ts_read(struct work_struct *work)
{
char buf[6];
@@ -67,7 +73,7 @@ static void eeti_ts_read(struct work_struct *work)
mutex_lock(&priv->mutex);
- while (!gpio_get_value(irq_to_gpio(priv->irq)) && --to)
+ while (eeti_ts_irq_active(priv) && --to)
i2c_master_recv(priv->client, buf, sizeof(buf));
if (!to) {
@@ -140,8 +146,10 @@ static void eeti_ts_close(struct input_dev *dev)
static int __devinit eeti_ts_probe(struct i2c_client *client,
const struct i2c_device_id *idp)
{
+ struct eeti_ts_platform_data *pdata;
struct eeti_ts_priv *priv;
struct input_dev *input;
+ unsigned int irq_flags;
int err = -ENOMEM;
/* In contrast to what's described in the datasheet, there seems
@@ -180,6 +188,14 @@ static int __devinit eeti_ts_probe(struct i2c_client *client,
priv->input = input;
priv->irq = client->irq;
+ pdata = client->dev.platform_data;
+
+ if (pdata)
+ priv->irq_active_high = pdata->irq_active_high;
+
+ irq_flags = priv->irq_active_high ?
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+
INIT_WORK(&priv->work, eeti_ts_read);
i2c_set_clientdata(client, priv);
input_set_drvdata(input, priv);
@@ -188,7 +204,7 @@ static int __devinit eeti_ts_probe(struct i2c_client *client,
if (err)
goto err1;
- err = request_irq(priv->irq, eeti_ts_isr, IRQF_TRIGGER_FALLING,
+ err = request_irq(priv->irq, eeti_ts_isr, irq_flags,
client->name, priv);
if (err) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c
index 4d3139e2099..b4d7f63deff 100644
--- a/drivers/input/touchscreen/h3600_ts_input.c
+++ b/drivers/input/touchscreen/h3600_ts_input.c
@@ -148,9 +148,10 @@ unsigned int h3600_flite_power(struct input_dev *dev, enum flite_pwr pwr)
struct h3600_dev *ts = input_get_drvdata(dev);
/* Must be in this order */
- ts->serio->write(ts->serio, 1);
- ts->serio->write(ts->serio, pwr);
- ts->serio->write(ts->serio, brightness);
+ serio_write(ts->serio, 1);
+ serio_write(ts->serio, pwr);
+ serio_write(ts->serio, brightness);
+
return 0;
}
@@ -262,7 +263,7 @@ static int h3600ts_event(struct input_dev *dev, unsigned int type,
switch (type) {
case EV_LED: {
- // ts->serio->write(ts->serio, SOME_CMD);
+ // serio_write(ts->serio, SOME_CMD);
return 0;
}
}
diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c
index 4cc047a5116..8fc3b08deb3 100644
--- a/drivers/input/touchscreen/mainstone-wm97xx.c
+++ b/drivers/input/touchscreen/mainstone-wm97xx.c
@@ -31,9 +31,11 @@
#include <linux/interrupt.h>
#include <linux/wm97xx.h>
#include <linux/io.h>
+#include <linux/gpio.h>
+
#include <mach/regs-ac97.h>
-#define VERSION "0.13"
+#include <asm/mach-types.h>
struct continuous {
u16 id; /* codec id */
@@ -62,6 +64,7 @@ static const struct continuous cinfo[] = {
/* continuous speed index */
static int sp_idx;
static u16 last, tries;
+static int irq;
/*
* Pen sampling frequency (Hz) in continuous mode.
@@ -171,7 +174,7 @@ up:
static int wm97xx_acc_startup(struct wm97xx *wm)
{
- int idx = 0;
+ int idx = 0, ret = 0;
/* check we have a codec */
if (wm->ac97 == NULL)
@@ -191,18 +194,40 @@ static int wm97xx_acc_startup(struct wm97xx *wm)
"mainstone accelerated touchscreen driver, %d samples/sec\n",
cinfo[sp_idx].speed);
+ /* IRQ driven touchscreen is used on Palm hardware */
+ if (machine_is_palmt5() || machine_is_palmtx() || machine_is_palmld()) {
+ pen_int = 1;
+ irq = 27;
+ /* There is some obscure mutant of WM9712 interbred with WM9713
+ * used on Palm HW */
+ wm->variant = WM97xx_WM1613;
+ } else if (machine_is_mainstone() && pen_int)
+ irq = 4;
+
+ if (irq) {
+ ret = gpio_request(irq, "Touchscreen IRQ");
+ if (ret)
+ goto out;
+
+ ret = gpio_direction_input(irq);
+ if (ret) {
+ gpio_free(irq);
+ goto out;
+ }
+
+ wm->pen_irq = gpio_to_irq(irq);
+ set_irq_type(wm->pen_irq, IRQ_TYPE_EDGE_BOTH);
+ } else /* pen irq not supported */
+ pen_int = 0;
+
/* codec specific irq config */
if (pen_int) {
switch (wm->id) {
case WM9705_ID2:
- wm->pen_irq = IRQ_GPIO(4);
- set_irq_type(IRQ_GPIO(4), IRQ_TYPE_EDGE_BOTH);
break;
case WM9712_ID2:
case WM9713_ID2:
- /* enable pen down interrupt */
/* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */
- wm->pen_irq = MAINSTONE_AC97_IRQ;
wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
WM97XX_GPIO_POL_HIGH,
WM97XX_GPIO_STICKY,
@@ -220,23 +245,17 @@ static int wm97xx_acc_startup(struct wm97xx *wm)
}
}
- return 0;
+out:
+ return ret;
}
static void wm97xx_acc_shutdown(struct wm97xx *wm)
{
/* codec specific deconfig */
if (pen_int) {
- switch (wm->id & 0xffff) {
- case WM9705_ID2:
- wm->pen_irq = 0;
- break;
- case WM9712_ID2:
- case WM9713_ID2:
- /* disable interrupt */
- wm->pen_irq = 0;
- break;
- }
+ if (irq)
+ gpio_free(irq);
+ wm->pen_irq = 0;
}
}
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index 880f58c6a7c..7ef0d1420d3 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -21,15 +21,14 @@
*/
#include <linux/module.h>
-#include <linux/hrtimer.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/i2c/tsc2007.h>
-#define TS_POLL_DELAY (10 * 1000) /* ns delay before the first sample */
-#define TS_POLL_PERIOD (5 * 1000) /* ns delay between samples */
+#define TS_POLL_DELAY 1 /* ms delay between samples */
+#define TS_POLL_PERIOD 1 /* ms delay between samples */
#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
#define TSC2007_MEASURE_AUX (0x2 << 4)
@@ -70,17 +69,14 @@ struct ts_event {
struct tsc2007 {
struct input_dev *input;
char phys[32];
- struct hrtimer timer;
- struct ts_event tc;
+ struct delayed_work work;
struct i2c_client *client;
- spinlock_t lock;
-
u16 model;
u16 x_plate_ohms;
- unsigned pendown;
+ bool pendown;
int irq;
int (*get_pendown_state)(void);
@@ -109,52 +105,96 @@ static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
return val;
}
-static void tsc2007_send_event(void *tsc)
+static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
{
- struct tsc2007 *ts = tsc;
- u32 rt;
- u16 x, y, z1, z2;
+ /* y- still on; turn on only y+ (and ADC) */
+ tc->y = tsc2007_xfer(tsc, READ_Y);
+
+ /* turn y- off, x+ on, then leave in lowpower */
+ tc->x = tsc2007_xfer(tsc, READ_X);
+
+ /* turn y+ off, x- on; we'll use formula #1 */
+ tc->z1 = tsc2007_xfer(tsc, READ_Z1);
+ tc->z2 = tsc2007_xfer(tsc, READ_Z2);
- x = ts->tc.x;
- y = ts->tc.y;
- z1 = ts->tc.z1;
- z2 = ts->tc.z2;
+ /* Prepare for next touch reading - power down ADC, enable PENIRQ */
+ tsc2007_xfer(tsc, PWRDOWN);
+}
+
+static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
+{
+ u32 rt = 0;
/* range filtering */
- if (x == MAX_12BIT)
- x = 0;
+ if (tc->x == MAX_12BIT)
+ tc->x = 0;
- if (likely(x && z1)) {
+ if (likely(tc->x && tc->z1)) {
/* compute touch pressure resistance using equation #1 */
- rt = z2;
- rt -= z1;
- rt *= x;
- rt *= ts->x_plate_ohms;
- rt /= z1;
+ rt = tc->z2 - tc->z1;
+ rt *= tc->x;
+ rt *= tsc->x_plate_ohms;
+ rt /= tc->z1;
rt = (rt + 2047) >> 12;
- } else
- rt = 0;
+ }
+
+ return rt;
+}
+
+static void tsc2007_send_up_event(struct tsc2007 *tsc)
+{
+ struct input_dev *input = tsc->input;
- /* Sample found inconsistent by debouncing or pressure is beyond
- * the maximum. Don't report it to user space, repeat at least
- * once more the measurement
+ dev_dbg(&tsc->client->dev, "UP\n");
+
+ input_report_key(input, BTN_TOUCH, 0);
+ input_report_abs(input, ABS_PRESSURE, 0);
+ input_sync(input);
+}
+
+static void tsc2007_work(struct work_struct *work)
+{
+ struct tsc2007 *ts =
+ container_of(to_delayed_work(work), struct tsc2007, work);
+ struct ts_event tc;
+ u32 rt;
+
+ /*
+ * NOTE: We can't rely on the pressure to determine the pen down
+ * state, even though this controller has a pressure sensor.
+ * The pressure value can fluctuate for quite a while after
+ * lifting the pen and in some cases may not even settle at the
+ * expected value.
+ *
+ * The only safe way to check for the pen up condition is in the
+ * work function by reading the pen signal state (it's a GPIO
+ * and IRQ). Unfortunately such callback is not always available,
+ * in that case we have rely on the pressure anyway.
*/
+ if (ts->get_pendown_state) {
+ if (unlikely(!ts->get_pendown_state())) {
+ tsc2007_send_up_event(ts);
+ ts->pendown = false;
+ goto out;
+ }
+
+ dev_dbg(&ts->client->dev, "pen is still down\n");
+ }
+
+ tsc2007_read_values(ts, &tc);
+
+ rt = tsc2007_calculate_pressure(ts, &tc);
if (rt > MAX_12BIT) {
+ /*
+ * Sample found inconsistent by debouncing or pressure is
+ * beyond the maximum. Don't report it to user space,
+ * repeat at least once more the measurement.
+ */
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
+ goto out;
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
- HRTIMER_MODE_REL);
- return;
}
- /* NOTE: We can't rely on the pressure to determine the pen down
- * state, even this controller has a pressure sensor. The pressure
- * value can fluctuate for quite a while after lifting the pen and
- * in some cases may not even settle at the expected value.
- *
- * The only safe way to check for the pen up condition is in the
- * timer by reading the pen signal state (it's a GPIO _and_ IRQ).
- */
if (rt) {
struct input_dev *input = ts->input;
@@ -162,102 +202,74 @@ static void tsc2007_send_event(void *tsc)
dev_dbg(&ts->client->dev, "DOWN\n");
input_report_key(input, BTN_TOUCH, 1);
- ts->pendown = 1;
+ ts->pendown = true;
}
- input_report_abs(input, ABS_X, x);
- input_report_abs(input, ABS_Y, y);
+ input_report_abs(input, ABS_X, tc.x);
+ input_report_abs(input, ABS_Y, tc.y);
input_report_abs(input, ABS_PRESSURE, rt);
input_sync(input);
dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
- x, y, rt);
+ tc.x, tc.y, rt);
+
+ } else if (!ts->get_pendown_state && ts->pendown) {
+ /*
+ * We don't have callback to check pendown state, so we
+ * have to assume that since pressure reported is 0 the
+ * pen was lifted up.
+ */
+ tsc2007_send_up_event(ts);
+ ts->pendown = false;
}
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
- HRTIMER_MODE_REL);
-}
-
-static int tsc2007_read_values(struct tsc2007 *tsc)
-{
- /* y- still on; turn on only y+ (and ADC) */
- tsc->tc.y = tsc2007_xfer(tsc, READ_Y);
-
- /* turn y- off, x+ on, then leave in lowpower */
- tsc->tc.x = tsc2007_xfer(tsc, READ_X);
-
- /* turn y+ off, x- on; we'll use formula #1 */
- tsc->tc.z1 = tsc2007_xfer(tsc, READ_Z1);
- tsc->tc.z2 = tsc2007_xfer(tsc, READ_Z2);
-
- /* power down */
- tsc2007_xfer(tsc, PWRDOWN);
-
- return 0;
-}
-
-static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle)
-{
- struct tsc2007 *ts = container_of(handle, struct tsc2007, timer);
- unsigned long flags;
-
- spin_lock_irqsave(&ts->lock, flags);
-
- if (unlikely(!ts->get_pendown_state() && ts->pendown)) {
- struct input_dev *input = ts->input;
-
- dev_dbg(&ts->client->dev, "UP\n");
-
- input_report_key(input, BTN_TOUCH, 0);
- input_report_abs(input, ABS_PRESSURE, 0);
- input_sync(input);
-
- ts->pendown = 0;
+ out:
+ if (ts->pendown)
+ schedule_delayed_work(&ts->work,
+ msecs_to_jiffies(TS_POLL_PERIOD));
+ else
enable_irq(ts->irq);
- } else {
- /* pen is still down, continue with the measurement */
- dev_dbg(&ts->client->dev, "pen is still down\n");
-
- tsc2007_read_values(ts);
- tsc2007_send_event(ts);
- }
-
- spin_unlock_irqrestore(&ts->lock, flags);
-
- return HRTIMER_NORESTART;
}
static irqreturn_t tsc2007_irq(int irq, void *handle)
{
struct tsc2007 *ts = handle;
- unsigned long flags;
-
- spin_lock_irqsave(&ts->lock, flags);
- if (likely(ts->get_pendown_state())) {
+ if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {
disable_irq_nosync(ts->irq);
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY),
- HRTIMER_MODE_REL);
+ schedule_delayed_work(&ts->work,
+ msecs_to_jiffies(TS_POLL_DELAY));
}
if (ts->clear_penirq)
ts->clear_penirq();
- spin_unlock_irqrestore(&ts->lock, flags);
-
return IRQ_HANDLED;
}
-static int tsc2007_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static void tsc2007_free_irq(struct tsc2007 *ts)
+{
+ free_irq(ts->irq, ts);
+ if (cancel_delayed_work_sync(&ts->work)) {
+ /*
+ * Work was pending, therefore we need to enable
+ * IRQ here to balance the disable_irq() done in the
+ * interrupt handler.
+ */
+ enable_irq(ts->irq);
+ }
+}
+
+static int __devinit tsc2007_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
struct tsc2007 *ts;
struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data;
struct input_dev *input_dev;
int err;
- if (!pdata || !pdata->get_pendown_state) {
+ if (!pdata) {
dev_err(&client->dev, "platform data is required!\n");
return -EINVAL;
}
@@ -274,22 +286,15 @@ static int tsc2007_probe(struct i2c_client *client,
}
ts->client = client;
- i2c_set_clientdata(client, ts);
-
+ ts->irq = client->irq;
ts->input = input_dev;
-
- hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- ts->timer.function = tsc2007_timer;
-
- spin_lock_init(&ts->lock);
+ INIT_DELAYED_WORK(&ts->work, tsc2007_work);
ts->model = pdata->model;
ts->x_plate_ohms = pdata->x_plate_ohms;
ts->get_pendown_state = pdata->get_pendown_state;
ts->clear_penirq = pdata->clear_penirq;
- pdata->init_platform_hw();
-
snprintf(ts->phys, sizeof(ts->phys),
"%s/input0", dev_name(&client->dev));
@@ -304,9 +309,8 @@ static int tsc2007_probe(struct i2c_client *client,
input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
- tsc2007_read_values(ts);
-
- ts->irq = client->irq;
+ if (pdata->init_platform_hw)
+ pdata->init_platform_hw();
err = request_irq(ts->irq, tsc2007_irq, 0,
client->dev.driver->name, ts);
@@ -315,33 +319,39 @@ static int tsc2007_probe(struct i2c_client *client,
goto err_free_mem;
}
+ /* Prepare for touch readings - power down ADC and enable PENIRQ */
+ err = tsc2007_xfer(ts, PWRDOWN);
+ if (err < 0)
+ goto err_free_irq;
+
err = input_register_device(input_dev);
if (err)
goto err_free_irq;
- dev_info(&client->dev, "registered with irq (%d)\n", ts->irq);
+ i2c_set_clientdata(client, ts);
return 0;
err_free_irq:
- free_irq(ts->irq, ts);
- hrtimer_cancel(&ts->timer);
+ tsc2007_free_irq(ts);
+ if (pdata->exit_platform_hw)
+ pdata->exit_platform_hw();
err_free_mem:
input_free_device(input_dev);
kfree(ts);
return err;
}
-static int tsc2007_remove(struct i2c_client *client)
+static int __devexit tsc2007_remove(struct i2c_client *client)
{
struct tsc2007 *ts = i2c_get_clientdata(client);
- struct tsc2007_platform_data *pdata;
+ struct tsc2007_platform_data *pdata = client->dev.platform_data;
- pdata = client->dev.platform_data;
- pdata->exit_platform_hw();
+ tsc2007_free_irq(ts);
+
+ if (pdata->exit_platform_hw)
+ pdata->exit_platform_hw();
- free_irq(ts->irq, ts);
- hrtimer_cancel(&ts->timer);
input_unregister_device(ts->input);
kfree(ts);
@@ -362,7 +372,7 @@ static struct i2c_driver tsc2007_driver = {
},
.id_table = tsc2007_idtable,
.probe = tsc2007_probe,
- .remove = tsc2007_remove,
+ .remove = __devexit_p(tsc2007_remove),
};
static int __init tsc2007_init(void)
diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c
index 3a7a58222f8..095f84b1f56 100644
--- a/drivers/input/touchscreen/ucb1400_ts.c
+++ b/drivers/input/touchscreen/ucb1400_ts.c
@@ -128,9 +128,10 @@ static inline unsigned int ucb1400_ts_read_yres(struct ucb1400_ts *ucb)
return ucb1400_adc_read(ucb->ac97, 0, adcsync);
}
-static inline int ucb1400_ts_pen_down(struct snd_ac97 *ac97)
+static inline int ucb1400_ts_pen_up(struct snd_ac97 *ac97)
{
unsigned short val = ucb1400_reg_read(ac97, UCB_TS_CR);
+
return val & (UCB_TS_CR_TSPX_LOW | UCB_TS_CR_TSMX_LOW);
}
@@ -209,7 +210,7 @@ static int ucb1400_ts_thread(void *_ucb)
msleep(10);
- if (ucb1400_ts_pen_down(ucb->ac97)) {
+ if (ucb1400_ts_pen_up(ucb->ac97)) {
ucb1400_ts_irq_enable(ucb->ac97);
/*
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index fb7cb9bdfbd..68ece5801a5 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -13,6 +13,7 @@
* - IdealTEK URTC1000
* - General Touch
* - GoTop Super_Q2/GogoPen/PenPower tablets
+ * - JASTEC USB touch controller/DigiTech DTR-02U
*
* Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch>
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
@@ -118,6 +119,8 @@ enum {
DEVTYPE_IDEALTEK,
DEVTYPE_GENERAL_TOUCH,
DEVTYPE_GOTOP,
+ DEVTYPE_JASTEC,
+ DEVTYPE_E2I,
};
#define USB_DEVICE_HID_CLASS(vend, prod) \
@@ -191,11 +194,51 @@ static struct usb_device_id usbtouch_devices[] = {
{USB_DEVICE(0x08f2, 0x00f4), .driver_info = DEVTYPE_GOTOP},
#endif
+#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
+ {USB_DEVICE(0x0f92, 0x0001), .driver_info = DEVTYPE_JASTEC},
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_E2I
+ {USB_DEVICE(0x1ac7, 0x0001), .driver_info = DEVTYPE_E2I},
+#endif
{}
};
/*****************************************************************************
+ * e2i Part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_E2I
+static int e2i_init(struct usbtouch_usb *usbtouch)
+{
+ int ret;
+
+ ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
+ 0x01, 0x02, 0x0000, 0x0081,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+ dbg("%s - usb_control_msg - E2I_RESET - bytes|err: %d",
+ __func__, ret);
+ return ret;
+}
+
+static int e2i_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ int tmp = (pkt[0] << 8) | pkt[1];
+ dev->x = (pkt[2] << 8) | pkt[3];
+ dev->y = (pkt[4] << 8) | pkt[5];
+
+ tmp = tmp - 0xA000;
+ dev->touch = (tmp > 0);
+ dev->press = (tmp > 0 ? tmp : 0);
+
+ return 1;
+}
+#endif
+
+
+/*****************************************************************************
* eGalax part
*/
@@ -559,6 +602,21 @@ static int gotop_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
dev->x = ((pkt[1] & 0x38) << 4) | pkt[2];
dev->y = ((pkt[1] & 0x07) << 7) | pkt[3];
dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * JASTEC Part
+ */
+#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
+static int jastec_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ dev->x = ((pkt[0] & 0x3f) << 6) | (pkt[2] & 0x3f);
+ dev->y = ((pkt[1] & 0x3f) << 6) | (pkt[3] & 0x3f);
+ dev->touch = (pkt[0] & 0x40) >> 6;
+
return 1;
}
#endif
@@ -702,6 +760,29 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.read_data = gotop_read_data,
},
#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_JASTEC
+ [DEVTYPE_JASTEC] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 4,
+ .read_data = jastec_read_data,
+ },
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_USB_E2I
+ [DEVTYPE_E2I] = {
+ .min_xc = 0x0,
+ .max_xc = 0x7fff,
+ .min_yc = 0x0,
+ .max_yc = 0x7fff,
+ .rept_size = 6,
+ .init = e2i_init,
+ .read_data = e2i_read_data,
+ },
+#endif
};
diff --git a/drivers/input/touchscreen/w90p910_ts.c b/drivers/input/touchscreen/w90p910_ts.c
index 6071f588257..6ccbdbbf33f 100644
--- a/drivers/input/touchscreen/w90p910_ts.c
+++ b/drivers/input/touchscreen/w90p910_ts.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/clk.h>
#include <linux/input.h>
#include <linux/interrupt.h>
@@ -47,8 +48,8 @@ enum ts_state {
struct w90p910_ts {
struct input_dev *input;
struct timer_list timer;
+ struct clk *clk;
int irq_num;
- void __iomem *clocken;
void __iomem *ts_reg;
spinlock_t lock;
enum ts_state state;
@@ -166,8 +167,7 @@ static int w90p910_open(struct input_dev *dev)
unsigned long val;
/* enable the ADC clock */
- val = __raw_readl(w90p910_ts->clocken);
- __raw_writel(val | ADC_CLK_EN, w90p910_ts->clocken);
+ clk_enable(w90p910_ts->clk);
__raw_writel(ADC_RST1, w90p910_ts->ts_reg);
msleep(1);
@@ -211,8 +211,7 @@ static void w90p910_close(struct input_dev *dev)
del_timer_sync(&w90p910_ts->timer);
/* stop the ADC clock */
- val = __raw_readl(w90p910_ts->clocken);
- __raw_writel(val & ~ADC_CLK_EN, w90p910_ts->clocken);
+ clk_disable(w90p910_ts->clk);
}
static int __devinit w90x900ts_probe(struct platform_device *pdev)
@@ -241,26 +240,24 @@ static int __devinit w90x900ts_probe(struct platform_device *pdev)
goto fail1;
}
- if (!request_mem_region(res->start, res->end - res->start + 1,
+ if (!request_mem_region(res->start, resource_size(res),
pdev->name)) {
err = -EBUSY;
goto fail1;
}
- w90p910_ts->ts_reg = ioremap(res->start, res->end - res->start + 1);
+ w90p910_ts->ts_reg = ioremap(res->start, resource_size(res));
if (!w90p910_ts->ts_reg) {
err = -ENOMEM;
goto fail2;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!res) {
- err = -ENXIO;
+ w90p910_ts->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(w90p910_ts->clk)) {
+ err = PTR_ERR(w90p910_ts->clk);
goto fail3;
}
- w90p910_ts->clocken = (void __iomem *)res->start;
-
input_dev->name = "W90P910 TouchScreen";
input_dev->phys = "w90p910ts/event0";
input_dev->id.bustype = BUS_HOST;
@@ -283,20 +280,21 @@ static int __devinit w90x900ts_probe(struct platform_device *pdev)
if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt,
IRQF_DISABLED, "w90p910ts", w90p910_ts)) {
err = -EBUSY;
- goto fail3;
+ goto fail4;
}
err = input_register_device(w90p910_ts->input);
if (err)
- goto fail4;
+ goto fail5;
platform_set_drvdata(pdev, w90p910_ts);
return 0;
-fail4: free_irq(w90p910_ts->irq_num, w90p910_ts);
+fail5: free_irq(w90p910_ts->irq_num, w90p910_ts);
+fail4: clk_put(w90p910_ts->clk);
fail3: iounmap(w90p910_ts->ts_reg);
-fail2: release_mem_region(res->start, res->end - res->start + 1);
+fail2: release_mem_region(res->start, resource_size(res));
fail1: input_free_device(input_dev);
kfree(w90p910_ts);
return err;
@@ -311,8 +309,10 @@ static int __devexit w90x900ts_remove(struct platform_device *pdev)
del_timer_sync(&w90p910_ts->timer);
iounmap(w90p910_ts->ts_reg);
+ clk_put(w90p910_ts->clk);
+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(res->start, res->end - res->start + 1);
+ release_mem_region(res->start, resource_size(res));
input_unregister_device(w90p910_ts->input);
kfree(w90p910_ts);
@@ -326,7 +326,7 @@ static struct platform_driver w90x900ts_driver = {
.probe = w90x900ts_probe,
.remove = __devexit_p(w90x900ts_remove),
.driver = {
- .name = "w90x900-ts",
+ .name = "nuc900-ts",
.owner = THIS_MODULE,
},
};
@@ -347,4 +347,4 @@ module_exit(w90x900ts_exit);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_DESCRIPTION("w90p910 touch screen driver!");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:w90p910-ts");
+MODULE_ALIAS("platform:nuc900-ts");
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
index 2f33a016764..56dc35c94bb 100644
--- a/drivers/input/touchscreen/wacom_w8001.c
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -25,18 +25,16 @@ MODULE_AUTHOR("Jaya Kumar <jayakumar.lkml@gmail.com>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-/*
- * Definitions & global arrays.
- */
-
#define W8001_MAX_LENGTH 11
-#define W8001_PACKET_LEN 11
-#define W8001_LEAD_MASK 0x80
-#define W8001_LEAD_BYTE 0x80
-#define W8001_TAB_MASK 0x40
-#define W8001_TAB_BYTE 0x40
+#define W8001_LEAD_MASK 0x80
+#define W8001_LEAD_BYTE 0x80
+#define W8001_TAB_MASK 0x40
+#define W8001_TAB_BYTE 0x40
-#define W8001_QUERY_PACKET 0x20
+#define W8001_QUERY_PACKET 0x20
+
+#define W8001_CMD_START '1'
+#define W8001_CMD_QUERY '*'
struct w8001_coord {
u8 rdy;
@@ -57,18 +55,19 @@ struct w8001_coord {
struct w8001 {
struct input_dev *dev;
struct serio *serio;
- struct mutex cmd_mutex;
struct completion cmd_done;
int id;
int idx;
- unsigned char expected_packet;
+ unsigned char response_type;
+ unsigned char response[W8001_MAX_LENGTH];
unsigned char data[W8001_MAX_LENGTH];
- unsigned char response[W8001_PACKET_LEN];
char phys[32];
};
-static int parse_data(u8 *data, struct w8001_coord *coord)
+static void parse_data(u8 *data, struct w8001_coord *coord)
{
+ memset(coord, 0, sizeof(*coord));
+
coord->rdy = data[0] & 0x20;
coord->tsw = data[0] & 0x01;
coord->f1 = data[0] & 0x02;
@@ -87,15 +86,15 @@ static int parse_data(u8 *data, struct w8001_coord *coord)
coord->tilt_x = data[7] & 0x7F;
coord->tilt_y = data[8] & 0x7F;
-
- return 0;
}
-static void w8001_process_data(struct w8001 *w8001, unsigned char data)
+static irqreturn_t w8001_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
{
+ struct w8001 *w8001 = serio_get_drvdata(serio);
struct input_dev *dev = w8001->dev;
- u8 tmp;
struct w8001_coord coord;
+ unsigned char tmp;
w8001->data[w8001->idx] = data;
switch (w8001->idx++) {
@@ -105,12 +104,13 @@ static void w8001_process_data(struct w8001 *w8001, unsigned char data)
w8001->idx = 0;
}
break;
+
case 8:
tmp = w8001->data[0] & W8001_TAB_MASK;
if (unlikely(tmp == W8001_TAB_BYTE))
break;
+
w8001->idx = 0;
- memset(&coord, 0, sizeof(coord));
parse_data(w8001->data, &coord);
input_report_abs(dev, ABS_X, coord.x);
input_report_abs(dev, ABS_Y, coord.y);
@@ -118,86 +118,48 @@ static void w8001_process_data(struct w8001 *w8001, unsigned char data)
input_report_key(dev, BTN_TOUCH, coord.tsw);
input_sync(dev);
break;
+
case 10:
w8001->idx = 0;
- memcpy(w8001->response, &w8001->data, W8001_PACKET_LEN);
- w8001->expected_packet = W8001_QUERY_PACKET;
+ memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH);
+ w8001->response_type = W8001_QUERY_PACKET;
complete(&w8001->cmd_done);
break;
}
-}
-
-
-static irqreturn_t w8001_interrupt(struct serio *serio,
- unsigned char data, unsigned int flags)
-{
- struct w8001 *w8001 = serio_get_drvdata(serio);
-
- w8001_process_data(w8001, data);
return IRQ_HANDLED;
}
-static int w8001_async_command(struct w8001 *w8001, unsigned char *packet,
- int len)
-{
- int rc = -1;
- int i;
-
- mutex_lock(&w8001->cmd_mutex);
-
- for (i = 0; i < len; i++) {
- if (serio_write(w8001->serio, packet[i]))
- goto out;
- }
- rc = 0;
-
-out:
- mutex_unlock(&w8001->cmd_mutex);
- return rc;
-}
-
-static int w8001_command(struct w8001 *w8001, unsigned char *packet, int len)
+static int w8001_command(struct w8001 *w8001, unsigned char command,
+ bool wait_response)
{
- int rc = -1;
- int i;
+ int rc;
- mutex_lock(&w8001->cmd_mutex);
-
- serio_pause_rx(w8001->serio);
+ w8001->response_type = 0;
init_completion(&w8001->cmd_done);
- serio_continue_rx(w8001->serio);
-
- for (i = 0; i < len; i++) {
- if (serio_write(w8001->serio, packet[i]))
- goto out;
- }
- wait_for_completion_timeout(&w8001->cmd_done, HZ);
+ rc = serio_write(w8001->serio, command);
+ if (rc == 0 && wait_response) {
- if (w8001->expected_packet == W8001_QUERY_PACKET) {
- /* We are back in reporting mode, the query was ACKed */
- memcpy(packet, w8001->response, W8001_PACKET_LEN);
- rc = 0;
+ wait_for_completion_timeout(&w8001->cmd_done, HZ);
+ if (w8001->response_type != W8001_QUERY_PACKET)
+ rc = -EIO;
}
-out:
- mutex_unlock(&w8001->cmd_mutex);
return rc;
}
static int w8001_setup(struct w8001 *w8001)
{
- struct w8001_coord coord;
struct input_dev *dev = w8001->dev;
- unsigned char start[1] = { '1' };
- unsigned char query[11] = { '*' };
+ struct w8001_coord coord;
+ int error;
- if (w8001_command(w8001, query, 1))
- return -1;
+ error = w8001_command(w8001, W8001_CMD_QUERY, true);
+ if (error)
+ return error;
- memset(&coord, 0, sizeof(coord));
- parse_data(query, &coord);
+ parse_data(w8001->response, &coord);
input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
@@ -205,10 +167,7 @@ static int w8001_setup(struct w8001 *w8001)
input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
- if (w8001_async_command(w8001, start, 1))
- return -1;
-
- return 0;
+ return w8001_command(w8001, W8001_CMD_START, false);
}
/*
@@ -249,7 +208,6 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
w8001->serio = serio;
w8001->id = serio->id.id;
w8001->dev = input_dev;
- mutex_init(&w8001->cmd_mutex);
init_completion(&w8001->cmd_done);
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
@@ -269,7 +227,8 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
if (err)
goto fail2;
- if (w8001_setup(w8001))
+ err = w8001_setup(w8001);
+ if (err)
goto fail3;
err = input_register_device(w8001->dev);
diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
index 2957d48e004..252eb11fe9d 100644
--- a/drivers/input/touchscreen/wm97xx-core.c
+++ b/drivers/input/touchscreen/wm97xx-core.c
@@ -204,7 +204,7 @@ void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
else
reg &= ~gpio;
- if (wm->id == WM9712_ID2)
+ if (wm->id == WM9712_ID2 && wm->variant != WM97xx_WM1613)
wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1);
else
wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg);
@@ -307,7 +307,7 @@ static void wm97xx_pen_irq_worker(struct work_struct *work)
WM97XX_GPIO_13);
}
- if (wm->id == WM9712_ID2)
+ if (wm->id == WM9712_ID2 && wm->variant != WM97xx_WM1613)
wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status &
~WM97XX_GPIO_13) << 1);
else
@@ -582,6 +582,8 @@ static int wm97xx_probe(struct device *dev)
wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2);
+ wm->variant = WM97xx_GENERIC;
+
dev_info(wm->dev, "detected a wm97%02x codec\n", wm->id & 0xff);
switch (wm->id & 0xff) {
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 6f0d90d4a54..32d0b878ecc 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -64,6 +64,7 @@ struct multipath {
spinlock_t lock;
const char *hw_handler_name;
+ char *hw_handler_params;
unsigned nr_priority_groups;
struct list_head priority_groups;
unsigned pg_init_required; /* pg_init needs calling? */
@@ -219,6 +220,7 @@ static void free_multipath(struct multipath *m)
}
kfree(m->hw_handler_name);
+ kfree(m->hw_handler_params);
mempool_destroy(m->mpio_pool);
kfree(m);
}
@@ -615,6 +617,17 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
dm_put_device(ti, p->path.dev);
goto bad;
}
+
+ if (m->hw_handler_params) {
+ r = scsi_dh_set_params(q, m->hw_handler_params);
+ if (r < 0) {
+ ti->error = "unable to set hardware "
+ "handler parameters";
+ scsi_dh_detach(q);
+ dm_put_device(ti, p->path.dev);
+ goto bad;
+ }
+ }
}
r = ps->type->add_path(ps, &p->path, as->argc, as->argv, &ti->error);
@@ -705,6 +718,7 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
static int parse_hw_handler(struct arg_set *as, struct multipath *m)
{
unsigned hw_argc;
+ int ret;
struct dm_target *ti = m->ti;
static struct param _params[] = {
@@ -726,17 +740,33 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m)
request_module("scsi_dh_%s", m->hw_handler_name);
if (scsi_dh_handler_exist(m->hw_handler_name) == 0) {
ti->error = "unknown hardware handler type";
- kfree(m->hw_handler_name);
- m->hw_handler_name = NULL;
- return -EINVAL;
+ ret = -EINVAL;
+ goto fail;
}
- if (hw_argc > 1)
- DMWARN("Ignoring user-specified arguments for "
- "hardware handler \"%s\"", m->hw_handler_name);
+ if (hw_argc > 1) {
+ char *p;
+ int i, j, len = 4;
+
+ for (i = 0; i <= hw_argc - 2; i++)
+ len += strlen(as->argv[i]) + 1;
+ p = m->hw_handler_params = kzalloc(len, GFP_KERNEL);
+ if (!p) {
+ ti->error = "memory allocation failed";
+ ret = -ENOMEM;
+ goto fail;
+ }
+ j = sprintf(p, "%d", hw_argc - 1);
+ for (i = 0, p+=j+1; i <= hw_argc - 2; i++, p+=j+1)
+ j = sprintf(p, "%s", as->argv[i]);
+ }
consume(as, hw_argc - 1);
return 0;
+fail:
+ kfree(m->hw_handler_name);
+ m->hw_handler_name = NULL;
+ return ret;
}
static int parse_features(struct arg_set *as, struct multipath *m)
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index 33f179e66bf..cc9dc79b078 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -1129,7 +1129,7 @@ static int mirror_end_io(struct dm_target *ti, struct bio *bio,
if (error == -EOPNOTSUPP)
goto out;
- if ((error == -EWOULDBLOCK) && bio_rw_ahead(bio))
+ if ((error == -EWOULDBLOCK) && bio_rw_flagged(bio, BIO_RW_AHEAD))
goto out;
if (unlikely(error)) {
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index 3e563d25173..e0efc1adcaf 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -285,7 +285,7 @@ static int stripe_end_io(struct dm_target *ti, struct bio *bio,
if (!error)
return 0; /* I/O complete */
- if ((error == -EWOULDBLOCK) && bio_rw_ahead(bio))
+ if ((error == -EWOULDBLOCK) && bio_rw_flagged(bio, BIO_RW_AHEAD))
return error;
if (error == -EOPNOTSUPP)
@@ -336,7 +336,7 @@ static void stripe_io_hints(struct dm_target *ti,
unsigned chunk_size = (sc->chunk_mask + 1) << 9;
blk_limits_io_min(limits, chunk_size);
- limits->io_opt = chunk_size * sc->stripes;
+ blk_limits_io_opt(limits, chunk_size * sc->stripes);
}
static struct target_type stripe_target = {
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index b4845b14740..eee28fac210 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -130,7 +130,7 @@ struct mapped_device {
/*
* A list of ios that arrived while we were suspended.
*/
- atomic_t pending;
+ atomic_t pending[2];
wait_queue_head_t wait;
struct work_struct work;
struct bio_list deferred;
@@ -453,13 +453,14 @@ static void start_io_acct(struct dm_io *io)
{
struct mapped_device *md = io->md;
int cpu;
+ int rw = bio_data_dir(io->bio);
io->start_time = jiffies;
cpu = part_stat_lock();
part_round_stats(cpu, &dm_disk(md)->part0);
part_stat_unlock();
- dm_disk(md)->part0.in_flight = atomic_inc_return(&md->pending);
+ dm_disk(md)->part0.in_flight[rw] = atomic_inc_return(&md->pending[rw]);
}
static void end_io_acct(struct dm_io *io)
@@ -479,8 +480,9 @@ static void end_io_acct(struct dm_io *io)
* After this is decremented the bio must not be touched if it is
* a barrier.
*/
- dm_disk(md)->part0.in_flight = pending =
- atomic_dec_return(&md->pending);
+ dm_disk(md)->part0.in_flight[rw] = pending =
+ atomic_dec_return(&md->pending[rw]);
+ pending += atomic_read(&md->pending[rw^0x1]);
/* nudge anyone waiting on suspend queue */
if (!pending)
@@ -586,7 +588,7 @@ static void dec_pending(struct dm_io *io, int error)
*/
spin_lock_irqsave(&md->deferred_lock, flags);
if (__noflush_suspending(md)) {
- if (!bio_barrier(io->bio))
+ if (!bio_rw_flagged(io->bio, BIO_RW_BARRIER))
bio_list_add_head(&md->deferred,
io->bio);
} else
@@ -598,7 +600,7 @@ static void dec_pending(struct dm_io *io, int error)
io_error = io->error;
bio = io->bio;
- if (bio_barrier(bio)) {
+ if (bio_rw_flagged(bio, BIO_RW_BARRIER)) {
/*
* There can be just one barrier request so we use
* a per-device variable for error reporting.
@@ -1209,7 +1211,7 @@ static void __split_and_process_bio(struct mapped_device *md, struct bio *bio)
ci.map = dm_get_table(md);
if (unlikely(!ci.map)) {
- if (!bio_barrier(bio))
+ if (!bio_rw_flagged(bio, BIO_RW_BARRIER))
bio_io_error(bio);
else
if (!md->barrier_error)
@@ -1321,7 +1323,7 @@ static int _dm_request(struct request_queue *q, struct bio *bio)
* we have to queue this io for later.
*/
if (unlikely(test_bit(DMF_QUEUE_IO_TO_THREAD, &md->flags)) ||
- unlikely(bio_barrier(bio))) {
+ unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
up_read(&md->io_lock);
if (unlikely(test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags)) &&
@@ -1344,7 +1346,7 @@ static int dm_make_request(struct request_queue *q, struct bio *bio)
{
struct mapped_device *md = q->queuedata;
- if (unlikely(bio_barrier(bio))) {
+ if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
bio_endio(bio, -EOPNOTSUPP);
return 0;
}
@@ -1785,7 +1787,8 @@ static struct mapped_device *alloc_dev(int minor)
if (!md->disk)
goto bad_disk;
- atomic_set(&md->pending, 0);
+ atomic_set(&md->pending[0], 0);
+ atomic_set(&md->pending[1], 0);
init_waitqueue_head(&md->wait);
INIT_WORK(&md->work, dm_wq_work);
init_waitqueue_head(&md->eventq);
@@ -2088,7 +2091,8 @@ static int dm_wait_for_completion(struct mapped_device *md, int interruptible)
break;
}
spin_unlock_irqrestore(q->queue_lock, flags);
- } else if (!atomic_read(&md->pending))
+ } else if (!atomic_read(&md->pending[0]) &&
+ !atomic_read(&md->pending[1]))
break;
if (interruptible == TASK_INTERRUPTIBLE &&
@@ -2164,7 +2168,7 @@ static void dm_wq_work(struct work_struct *work)
if (dm_request_based(md))
generic_make_request(c);
else {
- if (bio_barrier(c))
+ if (bio_rw_flagged(c, BIO_RW_BARRIER))
process_barrier(md, c);
else
__split_and_process_bio(md, c);
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 5fe39c2a3d2..ea484290544 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -288,7 +288,7 @@ static int linear_make_request (struct request_queue *q, struct bio *bio)
sector_t start_sector;
int cpu;
- if (unlikely(bio_barrier(bio))) {
+ if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
bio_endio(bio, -EOPNOTSUPP);
return 0;
}
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index 7140909f666..89e76819f61 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -90,7 +90,7 @@ static void multipath_end_request(struct bio *bio, int error)
if (uptodate)
multipath_end_bh_io(mp_bh, 0);
- else if (!bio_rw_ahead(bio)) {
+ else if (!bio_rw_flagged(bio, BIO_RW_AHEAD)) {
/*
* oops, IO error:
*/
@@ -144,7 +144,7 @@ static int multipath_make_request (struct request_queue *q, struct bio * bio)
const int rw = bio_data_dir(bio);
int cpu;
- if (unlikely(bio_barrier(bio))) {
+ if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
bio_endio(bio, -EOPNOTSUPP);
return 0;
}
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index 898e2bdfee4..f845ed98fec 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -448,7 +448,7 @@ static int raid0_make_request(struct request_queue *q, struct bio *bio)
const int rw = bio_data_dir(bio);
int cpu;
- if (unlikely(bio_barrier(bio))) {
+ if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
bio_endio(bio, -EOPNOTSUPP);
return 0;
}
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 8726fd7ebce..ff7ed333599 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -782,8 +782,9 @@ static int make_request(struct request_queue *q, struct bio * bio)
struct bio_list bl;
struct page **behind_pages = NULL;
const int rw = bio_data_dir(bio);
- const int do_sync = bio_sync(bio);
- int cpu, do_barriers;
+ const bool do_sync = bio_rw_flagged(bio, BIO_RW_SYNCIO);
+ int cpu;
+ bool do_barriers;
mdk_rdev_t *blocked_rdev;
/*
@@ -797,7 +798,8 @@ static int make_request(struct request_queue *q, struct bio * bio)
md_write_start(mddev, bio); /* wait on superblock update early */
- if (unlikely(!mddev->barriers_work && bio_barrier(bio))) {
+ if (unlikely(!mddev->barriers_work &&
+ bio_rw_flagged(bio, BIO_RW_BARRIER))) {
if (rw == WRITE)
md_write_end(mddev);
bio_endio(bio, -EOPNOTSUPP);
@@ -925,7 +927,7 @@ static int make_request(struct request_queue *q, struct bio * bio)
atomic_set(&r1_bio->remaining, 0);
atomic_set(&r1_bio->behind_remaining, 0);
- do_barriers = bio_barrier(bio);
+ do_barriers = bio_rw_flagged(bio, BIO_RW_BARRIER);
if (do_barriers)
set_bit(R1BIO_Barrier, &r1_bio->state);
@@ -1600,7 +1602,7 @@ static void raid1d(mddev_t *mddev)
* We already have a nr_pending reference on these rdevs.
*/
int i;
- const int do_sync = bio_sync(r1_bio->master_bio);
+ const bool do_sync = bio_rw_flagged(r1_bio->master_bio, BIO_RW_SYNCIO);
clear_bit(R1BIO_BarrierRetry, &r1_bio->state);
clear_bit(R1BIO_Barrier, &r1_bio->state);
for (i=0; i < conf->raid_disks; i++)
@@ -1654,7 +1656,7 @@ static void raid1d(mddev_t *mddev)
(unsigned long long)r1_bio->sector);
raid_end_bio_io(r1_bio);
} else {
- const int do_sync = bio_sync(r1_bio->master_bio);
+ const bool do_sync = bio_rw_flagged(r1_bio->master_bio, BIO_RW_SYNCIO);
r1_bio->bios[r1_bio->read_disk] =
mddev->ro ? IO_BLOCKED : NULL;
r1_bio->read_disk = disk;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 3d9020cf6f6..d0a2152e064 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -796,12 +796,12 @@ static int make_request(struct request_queue *q, struct bio * bio)
int i;
int chunk_sects = conf->chunk_mask + 1;
const int rw = bio_data_dir(bio);
- const int do_sync = bio_sync(bio);
+ const bool do_sync = bio_rw_flagged(bio, BIO_RW_SYNCIO);
struct bio_list bl;
unsigned long flags;
mdk_rdev_t *blocked_rdev;
- if (unlikely(bio_barrier(bio))) {
+ if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER))) {
bio_endio(bio, -EOPNOTSUPP);
return 0;
}
@@ -1610,7 +1610,7 @@ static void raid10d(mddev_t *mddev)
raid_end_bio_io(r10_bio);
bio_put(bio);
} else {
- const int do_sync = bio_sync(r10_bio->master_bio);
+ const bool do_sync = bio_rw_flagged(r10_bio->master_bio, BIO_RW_SYNCIO);
bio_put(bio);
rdev = conf->mirrors[mirror].rdev;
if (printk_ratelimit())
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index b8a2c5dc67b..826eb346735 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -3606,7 +3606,7 @@ static int make_request(struct request_queue *q, struct bio * bi)
const int rw = bio_data_dir(bi);
int cpu, remaining;
- if (unlikely(bio_barrier(bi))) {
+ if (unlikely(bio_rw_flagged(bi, BIO_RW_BARRIER))) {
bio_endio(bi, -EOPNOTSUPP);
return 0;
}
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index 5d0ba4f5924..76fa2ee0b57 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -1015,9 +1015,9 @@ mpt_add_sge_64bit(void *pAddr, u32 flagslength, dma_addr_t dma_addr)
{
SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
pSge->Address.Low = cpu_to_le32
- (lower_32_bits((unsigned long)(dma_addr)));
+ (lower_32_bits(dma_addr));
pSge->Address.High = cpu_to_le32
- (upper_32_bits((unsigned long)dma_addr));
+ (upper_32_bits(dma_addr));
pSge->FlagsLength = cpu_to_le32
((flagslength | MPT_SGE_FLAGS_64_BIT_ADDRESSING));
}
@@ -1038,8 +1038,8 @@ mpt_add_sge_64bit_1078(void *pAddr, u32 flagslength, dma_addr_t dma_addr)
u32 tmp;
pSge->Address.Low = cpu_to_le32
- (lower_32_bits((unsigned long)(dma_addr)));
- tmp = (u32)(upper_32_bits((unsigned long)dma_addr));
+ (lower_32_bits(dma_addr));
+ tmp = (u32)(upper_32_bits(dma_addr));
/*
* 1078 errata workaround for the 36GB limitation
@@ -1101,7 +1101,7 @@ mpt_add_chain_64bit(void *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
pChain->NextChainOffset = next;
pChain->Address.Low = cpu_to_le32(tmp);
- tmp = (u32)(upper_32_bits((unsigned long)dma_addr));
+ tmp = (u32)(upper_32_bits(dma_addr));
pChain->Address.High = cpu_to_le32(tmp);
}
@@ -1297,12 +1297,8 @@ mpt_host_page_alloc(MPT_ADAPTER *ioc, pIOCInit_t ioc_init)
psge = (char *)&ioc_init->HostPageBufferSGE;
flags_length = MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_SYSTEM_ADDRESS |
- MPI_SGE_FLAGS_32_BIT_ADDRESSING |
MPI_SGE_FLAGS_HOST_TO_IOC |
MPI_SGE_FLAGS_END_OF_BUFFER;
- if (sizeof(dma_addr_t) == sizeof(u64)) {
- flags_length |= MPI_SGE_FLAGS_64_BIT_ADDRESSING;
- }
flags_length = flags_length << MPI_SGE_FLAGS_SHIFT;
flags_length |= ioc->HostPageBuffer_sz;
ioc->add_sge(psge, flags_length, ioc->HostPageBuffer_dma);
@@ -2224,8 +2220,6 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
int hard;
int rc=0;
int ii;
- u8 cb_idx;
- int handlers;
int ret = 0;
int reset_alt_ioc_active = 0;
int irq_allocated = 0;
@@ -2548,34 +2542,6 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
mpt_get_manufacturing_pg_0(ioc);
}
- /*
- * Call each currently registered protocol IOC reset handler
- * with post-reset indication.
- * NOTE: If we're doing _IOC_BRINGUP, there can be no
- * MptResetHandlers[] registered yet.
- */
- if (hard_reset_done) {
- rc = handlers = 0;
- for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
- if ((ret == 0) && MptResetHandlers[cb_idx]) {
- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- "Calling IOC post_reset handler #%d\n",
- ioc->name, cb_idx));
- rc += mpt_signal_reset(cb_idx, ioc, MPT_IOC_POST_RESET);
- handlers++;
- }
-
- if (alt_ioc_ready && MptResetHandlers[cb_idx]) {
- drsprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- "Calling IOC post_reset handler #%d\n",
- ioc->alt_ioc->name, cb_idx));
- rc += mpt_signal_reset(cb_idx, ioc->alt_ioc, MPT_IOC_POST_RESET);
- handlers++;
- }
- }
- /* FIXME? Examine results here? */
- }
-
out:
if ((ret != 0) && irq_allocated) {
free_irq(ioc->pci_irq, ioc);
@@ -3938,6 +3904,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
int count = 0;
u32 diag1val = 0;
MpiFwHeader_t *cached_fw; /* Pointer to FW */
+ u8 cb_idx;
/* Clear any existing interrupts */
CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
@@ -3956,6 +3923,18 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
else
mdelay(1);
+ /*
+ * Call each currently registered protocol IOC reset handler
+ * with pre-reset indication.
+ * NOTE: If we're doing _IOC_BRINGUP, there can be no
+ * MptResetHandlers[] registered yet.
+ */
+ for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
+ if (MptResetHandlers[cb_idx])
+ (*(MptResetHandlers[cb_idx]))(ioc,
+ MPT_IOC_PRE_RESET);
+ }
+
for (count = 0; count < 60; count ++) {
doorbell = CHIPREG_READ32(&ioc->chip->Doorbell);
doorbell &= MPI_IOC_STATE_MASK;
@@ -4052,25 +4031,15 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
* NOTE: If we're doing _IOC_BRINGUP, there can be no
* MptResetHandlers[] registered yet.
*/
- {
- u8 cb_idx;
- int r = 0;
-
- for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
- if (MptResetHandlers[cb_idx]) {
- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- "Calling IOC pre_reset handler #%d\n",
- ioc->name, cb_idx));
- r += mpt_signal_reset(cb_idx, ioc, MPT_IOC_PRE_RESET);
- if (ioc->alt_ioc) {
- dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
- "Calling alt-%s pre_reset handler #%d\n",
- ioc->name, ioc->alt_ioc->name, cb_idx));
- r += mpt_signal_reset(cb_idx, ioc->alt_ioc, MPT_IOC_PRE_RESET);
- }
+ for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
+ if (MptResetHandlers[cb_idx]) {
+ mpt_signal_reset(cb_idx,
+ ioc, MPT_IOC_PRE_RESET);
+ if (ioc->alt_ioc) {
+ mpt_signal_reset(cb_idx,
+ ioc->alt_ioc, MPT_IOC_PRE_RESET);
}
}
- /* FIXME? Examine results here? */
}
if (ioc->cached_fw)
@@ -6956,7 +6925,7 @@ EXPORT_SYMBOL(mpt_halt_firmware);
int
mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
{
- int rc;
+ int rc;
u8 cb_idx;
unsigned long flags;
unsigned long time_count;
@@ -6982,8 +6951,6 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
ioc->alt_ioc->ioc_reset_in_progress = 1;
spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
- /* FIXME: If do_ioc_recovery fails, repeat....
- */
/* The SCSI driver needs to adjust timeouts on all current
* commands prior to the diagnostic reset being issued.
@@ -7020,6 +6987,15 @@ mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag)
}
spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);
+ for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--) {
+ if (MptResetHandlers[cb_idx]) {
+ mpt_signal_reset(cb_idx, ioc, MPT_IOC_POST_RESET);
+ if (ioc->alt_ioc)
+ mpt_signal_reset(cb_idx,
+ ioc->alt_ioc, MPT_IOC_POST_RESET);
+ }
+ }
+
dtmprintk(ioc,
printk(MYIOC_s_DEBUG_FMT
"HardResetHandler: completed (%d seconds): %s\n", ioc->name,
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index 1c8514dc31c..8dd4d219e43 100644
--- a/drivers/message/fusion/mptbase.h
+++ b/drivers/message/fusion/mptbase.h
@@ -76,8 +76,8 @@
#define COPYRIGHT "Copyright (c) 1999-2008 " MODULEAUTHOR
#endif
-#define MPT_LINUX_VERSION_COMMON "3.04.10"
-#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.09"
+#define MPT_LINUX_VERSION_COMMON "3.04.12"
+#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.12"
#define WHAT_MAGIC_STRING "@" "(" "#" ")"
#define show_mptmod_ver(s,ver) \
@@ -157,8 +157,9 @@
/*
* Try to keep these at 2^N-1
*/
-#define MPT_FC_CAN_QUEUE 127
+#define MPT_FC_CAN_QUEUE 1024
#define MPT_SCSI_CAN_QUEUE 127
+#define MPT_SAS_CAN_QUEUE 127
/*
* Set the MAX_SGE value based on user input.
@@ -879,23 +880,9 @@ typedef enum {
typedef struct _MPT_SCSI_HOST {
MPT_ADAPTER *ioc;
- int port;
- u32 pad0;
- MPT_LOCAL_REPLY *pLocal; /* used for internal commands */
- struct timer_list timer;
- /* Pool of memory for holding SCpnts before doing
- * OS callbacks. freeQ is the free pool.
- */
- u8 negoNvram; /* DV disabled, nego NVRAM */
- u8 pad1;
- u8 rsvd[2];
- MPT_FRAME_HDR *cmdPtr; /* Ptr to nonOS request */
- struct scsi_cmnd *abortSCpnt;
- MPT_LOCAL_REPLY localReply; /* internal cmd reply struct */
ushort sel_timeout[MPT_MAX_FC_DEVICES];
char *info_kbuf;
long last_queue_full;
- u16 tm_iocstatus;
u16 spi_pending;
struct list_head target_reset_list;
} MPT_SCSI_HOST;
diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c
index e61df133a59..ebf6ae024da 100644
--- a/drivers/message/fusion/mptfc.c
+++ b/drivers/message/fusion/mptfc.c
@@ -1288,25 +1288,6 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiLookup @ %p\n",
ioc->name, ioc->ScsiLookup));
- /* Clear the TM flags
- */
- hd->abortSCpnt = NULL;
-
- /* Clear the pointer used to store
- * single-threaded commands, i.e., those
- * issued during a bus scan, dv and
- * configuration pages.
- */
- hd->cmdPtr = NULL;
-
- /* Initialize this SCSI Hosts' timers
- * To use, set the timer expires field
- * and add_timer
- */
- init_timer(&hd->timer);
- hd->timer.data = (unsigned long) hd;
- hd->timer.function = mptscsih_timer_expired;
-
hd->last_queue_full = 0;
sh->transportt = mptfc_transport_template;
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index 55ff25244af..83873e3d0ce 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -72,6 +72,7 @@
*/
#define MPTSAS_RAID_CHANNEL 1
+#define SAS_CONFIG_PAGE_TIMEOUT 30
MODULE_AUTHOR(MODULEAUTHOR);
MODULE_DESCRIPTION(my_NAME);
MODULE_LICENSE("GPL");
@@ -324,7 +325,6 @@ mptsas_cleanup_fw_event_q(MPT_ADAPTER *ioc)
{
struct fw_event_work *fw_event, *next;
struct mptsas_target_reset_event *target_reset_list, *n;
- u8 flush_q;
MPT_SCSI_HOST *hd = shost_priv(ioc->sh);
/* flush the target_reset_list */
@@ -344,15 +344,10 @@ mptsas_cleanup_fw_event_q(MPT_ADAPTER *ioc)
!ioc->fw_event_q || in_interrupt())
return;
- flush_q = 0;
list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) {
if (cancel_delayed_work(&fw_event->work))
mptsas_free_fw_event(ioc, fw_event);
- else
- flush_q = 1;
}
- if (flush_q)
- flush_workqueue(ioc->fw_event_q);
}
@@ -661,7 +656,7 @@ mptsas_add_device_component_starget_ir(MPT_ADAPTER *ioc,
cfg.pageAddr = starget->id;
cfg.cfghdr.hdr = &hdr;
cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
- cfg.timeout = 10;
+ cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
if (mpt_config(ioc, &cfg) != 0)
goto out;
@@ -851,7 +846,13 @@ mptsas_setup_wide_ports(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
port_details->num_phys--;
port_details->phy_bitmask &= ~ (1 << phy_info->phy_id);
memset(&phy_info->attached, 0, sizeof(struct mptsas_devinfo));
- sas_port_delete_phy(port_details->port, phy_info->phy);
+ if (phy_info->phy) {
+ devtprintk(ioc, dev_printk(KERN_DEBUG,
+ &phy_info->phy->dev, MYIOC_s_FMT
+ "delete phy %d, phy-obj (0x%p)\n", ioc->name,
+ phy_info->phy_id, phy_info->phy));
+ sas_port_delete_phy(port_details->port, phy_info->phy);
+ }
phy_info->port_details = NULL;
}
@@ -1272,7 +1273,6 @@ mptsas_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
}
mptsas_cleanup_fw_event_q(ioc);
mptsas_queue_rescan(ioc);
- mptsas_fw_event_on(ioc);
break;
default:
break;
@@ -1318,7 +1318,7 @@ mptsas_sas_enclosure_pg0(MPT_ADAPTER *ioc, struct mptsas_enclosure *enclosure,
cfg.pageAddr = form + form_specific;
cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
cfg.dir = 0; /* read */
- cfg.timeout = 10;
+ cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
error = mpt_config(ioc, &cfg);
if (error)
@@ -1592,6 +1592,7 @@ mptsas_firmware_event_work(struct work_struct *work)
mptsas_scan_sas_topology(ioc);
ioc->in_rescan = 0;
mptsas_free_fw_event(ioc, fw_event);
+ mptsas_fw_event_on(ioc);
return;
}
@@ -1891,7 +1892,7 @@ static struct scsi_host_template mptsas_driver_template = {
.eh_bus_reset_handler = mptscsih_bus_reset,
.eh_host_reset_handler = mptscsih_host_reset,
.bios_param = mptscsih_bios_param,
- .can_queue = MPT_FC_CAN_QUEUE,
+ .can_queue = MPT_SAS_CAN_QUEUE,
.this_id = -1,
.sg_tablesize = MPT_SCSI_SG_DEPTH,
.max_sectors = 8192,
@@ -1926,7 +1927,7 @@ static int mptsas_get_linkerrors(struct sas_phy *phy)
cfg.pageAddr = phy->identify.phy_identifier;
cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
cfg.dir = 0; /* read */
- cfg.timeout = 10;
+ cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
error = mpt_config(ioc, &cfg);
if (error)
@@ -2278,7 +2279,7 @@ mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
cfg.pageAddr = 0;
cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
cfg.dir = 0; /* read */
- cfg.timeout = 10;
+ cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
error = mpt_config(ioc, &cfg);
if (error)
@@ -2349,7 +2350,7 @@ mptsas_sas_io_unit_pg1(MPT_ADAPTER *ioc)
cfg.cfghdr.ehdr = &hdr;
cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
- cfg.timeout = 10;
+ cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
cfg.cfghdr.ehdr->PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
cfg.cfghdr.ehdr->ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;
cfg.cfghdr.ehdr->PageVersion = MPI_SASIOUNITPAGE1_PAGEVERSION;
@@ -2411,7 +2412,7 @@ mptsas_sas_phy_pg0(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info,
cfg.cfghdr.ehdr = &hdr;
cfg.dir = 0; /* read */
- cfg.timeout = 10;
+ cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
/* Get Phy Pg 0 for each Phy. */
cfg.physAddr = -1;
@@ -2479,7 +2480,7 @@ mptsas_sas_device_pg0(MPT_ADAPTER *ioc, struct mptsas_devinfo *device_info,
cfg.physAddr = -1;
cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
cfg.dir = 0; /* read */
- cfg.timeout = 10;
+ cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
memset(device_info, 0, sizeof(struct mptsas_devinfo));
error = mpt_config(ioc, &cfg);
@@ -2554,7 +2555,7 @@ mptsas_sas_expander_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info,
cfg.pageAddr = form + form_specific;
cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
cfg.dir = 0; /* read */
- cfg.timeout = 10;
+ cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
memset(port_info, 0, sizeof(struct mptsas_portinfo));
error = mpt_config(ioc, &cfg);
@@ -2635,7 +2636,7 @@ mptsas_sas_expander_pg1(MPT_ADAPTER *ioc, struct mptsas_phyinfo *phy_info,
cfg.pageAddr = form + form_specific;
cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
cfg.dir = 0; /* read */
- cfg.timeout = 10;
+ cfg.timeout = SAS_CONFIG_PAGE_TIMEOUT;
error = mpt_config(ioc, &cfg);
if (error)
@@ -3307,6 +3308,7 @@ mptsas_send_expander_event(struct fw_event_work *fw_event)
expander_data = (MpiEventDataSasExpanderStatusChange_t *)
fw_event->event_data;
memcpy(&sas_address, &expander_data->SASAddress, sizeof(__le64));
+ sas_address = le64_to_cpu(sas_address);
port_info = mptsas_find_portinfo_by_sas_address(ioc, sas_address);
if (expander_data->ReasonCode == MPI_EVENT_SAS_EXP_RC_ADDED) {
@@ -4760,10 +4762,9 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* set 16 byte cdb's */
sh->max_cmd_len = 16;
-
- sh->max_id = ioc->pfacts[0].PortSCSIID;
+ sh->can_queue = min_t(int, ioc->req_depth - 10, sh->can_queue);
+ sh->max_id = -1;
sh->max_lun = max_lun;
-
sh->transportt = mptsas_transport_template;
/* Required entry.
@@ -4821,25 +4822,6 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiLookup @ %p\n",
ioc->name, ioc->ScsiLookup));
- /* Clear the TM flags
- */
- hd->abortSCpnt = NULL;
-
- /* Clear the pointer used to store
- * single-threaded commands, i.e., those
- * issued during a bus scan, dv and
- * configuration pages.
- */
- hd->cmdPtr = NULL;
-
- /* Initialize this SCSI Hosts' timers
- * To use, set the timer expires field
- * and add_timer
- */
- init_timer(&hd->timer);
- hd->timer.data = (unsigned long) hd;
- hd->timer.function = mptscsih_timer_expired;
-
ioc->sas_data.ptClear = mpt_pt_clear;
hd->last_queue_full = 0;
diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
index 8440f78f696..c2957861450 100644
--- a/drivers/message/fusion/mptscsih.c
+++ b/drivers/message/fusion/mptscsih.c
@@ -628,6 +628,16 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
return 1;
}
+ if (ioc->bus_type == SAS) {
+ VirtDevice *vdevice = sc->device->hostdata;
+
+ if (!vdevice || !vdevice->vtarget ||
+ vdevice->vtarget->deleted) {
+ sc->result = DID_NO_CONNECT << 16;
+ goto out;
+ }
+ }
+
sc->host_scribble = NULL;
sc->result = DID_OK << 16; /* Set default reply as OK */
pScsiReq = (SCSIIORequest_t *) mf;
@@ -689,6 +699,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
switch(status) {
case MPI_IOCSTATUS_BUSY: /* 0x0002 */
+ case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: /* 0x0006 */
/* CHECKME!
* Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry)
* But not: DID_BUS_BUSY lest one risk
@@ -872,7 +883,6 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
case MPI_IOCSTATUS_INVALID_SGL: /* 0x0003 */
case MPI_IOCSTATUS_INTERNAL_ERROR: /* 0x0004 */
case MPI_IOCSTATUS_RESERVED: /* 0x0005 */
- case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: /* 0x0006 */
case MPI_IOCSTATUS_INVALID_FIELD: /* 0x0007 */
case MPI_IOCSTATUS_INVALID_STATE: /* 0x0008 */
case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
@@ -892,7 +902,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
#endif
} /* end of address reply case */
-
+out:
/* Unmap the DMA buffers, if any. */
scsi_dma_unmap(sc);
@@ -1729,9 +1739,6 @@ mptscsih_abort(struct scsi_cmnd * SCpnt)
*/
mf = MPT_INDEX_2_MFPTR(ioc, scpnt_idx);
ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext;
-
- hd->abortSCpnt = SCpnt;
-
retval = mptscsih_IssueTaskMgmt(hd,
MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
vdevice->vtarget->channel,
@@ -2293,7 +2300,10 @@ mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth)
else
max_depth = MPT_SCSI_CMD_PER_DEV_LOW;
} else
- max_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
+ max_depth = ioc->sh->can_queue;
+
+ if (!sdev->tagged_supported)
+ max_depth = 1;
if (qdepth > max_depth)
qdepth = max_depth;
@@ -2627,50 +2637,6 @@ mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,
return 1;
}
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/* mptscsih_timer_expired - Call back for timer process.
- * Used only for dv functionality.
- * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long
- *
- */
-void
-mptscsih_timer_expired(unsigned long data)
-{
- MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *) data;
- MPT_ADAPTER *ioc = hd->ioc;
-
- ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Timer Expired! Cmd %p\n", ioc->name, hd->cmdPtr));
-
- if (hd->cmdPtr) {
- MPIHeader_t *cmd = (MPIHeader_t *)hd->cmdPtr;
-
- if (cmd->Function == MPI_FUNCTION_SCSI_IO_REQUEST) {
- /* Desire to issue a task management request here.
- * TM requests MUST be single threaded.
- * If old eh code and no TM current, issue request.
- * If new eh code, do nothing. Wait for OS cmd timeout
- * for bus reset.
- */
- } else {
- /* Perform a FW reload */
- if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0) {
- printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", ioc->name);
- }
- }
- } else {
- /* This should NEVER happen */
- printk(MYIOC_s_WARN_FMT "Null cmdPtr!!!!\n", ioc->name);
- }
-
- /* No more processing.
- * TM call will generate an interrupt for SCSI TM Management.
- * The FW will reply to all outstanding commands, callback will finish cleanup.
- * Hard reset clean-up will free all resources.
- */
- ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Timer Expired Complete!\n", ioc->name));
-
- return;
-}
/**
* mptscsih_get_completion_code -
@@ -3265,6 +3231,5 @@ EXPORT_SYMBOL(mptscsih_scandv_complete);
EXPORT_SYMBOL(mptscsih_event_process);
EXPORT_SYMBOL(mptscsih_ioc_reset);
EXPORT_SYMBOL(mptscsih_change_queue_depth);
-EXPORT_SYMBOL(mptscsih_timer_expired);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h
index eb3f677528a..e0b33e04a33 100644
--- a/drivers/message/fusion/mptscsih.h
+++ b/drivers/message/fusion/mptscsih.h
@@ -129,7 +129,6 @@ extern int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRA
extern int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
extern int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
extern int mptscsih_change_queue_depth(struct scsi_device *sdev, int qdepth);
-extern void mptscsih_timer_expired(unsigned long data);
extern u8 mptscsih_raid_id_to_num(MPT_ADAPTER *ioc, u8 channel, u8 id);
extern int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id);
extern struct device_attribute *mptscsih_host_attrs[];
diff --git a/drivers/message/fusion/mptspi.c b/drivers/message/fusion/mptspi.c
index c5b808fd55b..69f4257419b 100644
--- a/drivers/message/fusion/mptspi.c
+++ b/drivers/message/fusion/mptspi.c
@@ -1472,28 +1472,7 @@ mptspi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiLookup @ %p\n",
ioc->name, ioc->ScsiLookup));
- /* Clear the TM flags
- */
- hd->abortSCpnt = NULL;
-
- /* Clear the pointer used to store
- * single-threaded commands, i.e., those
- * issued during a bus scan, dv and
- * configuration pages.
- */
- hd->cmdPtr = NULL;
-
- /* Initialize this SCSI Hosts' timers
- * To use, set the timer expires field
- * and add_timer
- */
- init_timer(&hd->timer);
- hd->timer.data = (unsigned long) hd;
- hd->timer.function = mptscsih_timer_expired;
-
ioc->spi_data.Saf_Te = mpt_saf_te;
-
- hd->negoNvram = MPT_SCSICFG_USE_NVRAM;
ddvprintk(ioc, printk(MYIOC_s_DEBUG_FMT
"saf_te %x\n",
ioc->name,
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 68ab39d7cb3..df1f86b5c83 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -233,6 +233,19 @@ config ISL29003
This driver can also be built as a module. If so, the module
will be called isl29003.
+config EP93XX_PWM
+ tristate "EP93xx PWM support"
+ depends on ARCH_EP93XX
+ help
+ This option enables device driver support for the PWM channels
+ on the Cirrus EP93xx processors. The EP9307 chip only has one
+ PWM channel all the others have two, the second channel is an
+ alternate function of the EGPIO14 pin. A sysfs interface is
+ provided to control the PWM channels.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ep93xx_pwm.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 36f733cd60e..f982d2ecfde 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_SGI_XP) += sgi-xp/
obj-$(CONFIG_SGI_GRU) += sgi-gru/
obj-$(CONFIG_HP_ILO) += hpilo.o
obj-$(CONFIG_ISL29003) += isl29003.o
+obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
obj-$(CONFIG_C2PORT) += c2port/
obj-y += eeprom/
obj-y += cb710/
diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c
index 348443bdb23..7b039306037 100644
--- a/drivers/misc/enclosure.c
+++ b/drivers/misc/enclosure.c
@@ -33,24 +33,44 @@ static DEFINE_MUTEX(container_list_lock);
static struct class enclosure_class;
/**
- * enclosure_find - find an enclosure given a device
- * @dev: the device to find for
+ * enclosure_find - find an enclosure given a parent device
+ * @dev: the parent to match against
+ * @start: Optional enclosure device to start from (NULL if none)
*
- * Looks through the list of registered enclosures to see
- * if it can find a match for a device. Returns NULL if no
- * enclosure is found. Obtains a reference to the enclosure class
- * device which must be released with device_put().
+ * Looks through the list of registered enclosures to find all those
+ * with @dev as a parent. Returns NULL if no enclosure is
+ * found. @start can be used as a starting point to obtain multiple
+ * enclosures per parent (should begin with NULL and then be set to
+ * each returned enclosure device). Obtains a reference to the
+ * enclosure class device which must be released with device_put().
+ * If @start is not NULL, a reference must be taken on it which is
+ * released before returning (this allows a loop through all
+ * enclosures to exit with only the reference on the enclosure of
+ * interest held). Note that the @dev may correspond to the actual
+ * device housing the enclosure, in which case no iteration via @start
+ * is required.
*/
-struct enclosure_device *enclosure_find(struct device *dev)
+struct enclosure_device *enclosure_find(struct device *dev,
+ struct enclosure_device *start)
{
struct enclosure_device *edev;
mutex_lock(&container_list_lock);
- list_for_each_entry(edev, &container_list, node) {
- if (edev->edev.parent == dev) {
- get_device(&edev->edev);
- mutex_unlock(&container_list_lock);
- return edev;
+ edev = list_prepare_entry(start, &container_list, node);
+ if (start)
+ put_device(&start->edev);
+
+ list_for_each_entry_continue(edev, &container_list, node) {
+ struct device *parent = edev->edev.parent;
+ /* parent might not be immediate, so iterate up to
+ * the root of the tree if necessary */
+ while (parent) {
+ if (parent == dev) {
+ get_device(&edev->edev);
+ mutex_unlock(&container_list_lock);
+ return edev;
+ }
+ parent = parent->parent;
}
}
mutex_unlock(&container_list_lock);
@@ -295,6 +315,9 @@ int enclosure_add_device(struct enclosure_device *edev, int component,
cdev = &edev->component[component];
+ if (cdev->dev == dev)
+ return -EEXIST;
+
if (cdev->dev)
enclosure_remove_links(cdev);
@@ -312,19 +335,25 @@ EXPORT_SYMBOL_GPL(enclosure_add_device);
* Returns zero on success or an error.
*
*/
-int enclosure_remove_device(struct enclosure_device *edev, int component)
+int enclosure_remove_device(struct enclosure_device *edev, struct device *dev)
{
struct enclosure_component *cdev;
+ int i;
- if (!edev || component >= edev->components)
+ if (!edev || !dev)
return -EINVAL;
- cdev = &edev->component[component];
-
- device_del(&cdev->cdev);
- put_device(cdev->dev);
- cdev->dev = NULL;
- return device_add(&cdev->cdev);
+ for (i = 0; i < edev->components; i++) {
+ cdev = &edev->component[i];
+ if (cdev->dev == dev) {
+ enclosure_remove_links(cdev);
+ device_del(&cdev->cdev);
+ put_device(dev);
+ cdev->dev = NULL;
+ return device_add(&cdev->cdev);
+ }
+ }
+ return -ENODEV;
}
EXPORT_SYMBOL_GPL(enclosure_remove_device);
diff --git a/drivers/misc/ep93xx_pwm.c b/drivers/misc/ep93xx_pwm.c
new file mode 100644
index 00000000000..ba4694169d7
--- /dev/null
+++ b/drivers/misc/ep93xx_pwm.c
@@ -0,0 +1,384 @@
+/*
+ * Simple PWM driver for EP93XX
+ *
+ * (c) Copyright 2009 Matthieu Crapet <mcrapet@gmail.com>
+ * (c) Copyright 2009 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * EP9307 has only one channel:
+ * - PWMOUT
+ *
+ * EP9301/02/12/15 have two channels:
+ * - PWMOUT
+ * - PWMOUT1 (alternate function for EGPIO14)
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <mach/platform.h>
+
+#define EP93XX_PWMx_TERM_COUNT 0x00
+#define EP93XX_PWMx_DUTY_CYCLE 0x04
+#define EP93XX_PWMx_ENABLE 0x08
+#define EP93XX_PWMx_INVERT 0x0C
+
+#define EP93XX_PWM_MAX_COUNT 0xFFFF
+
+struct ep93xx_pwm {
+ void __iomem *mmio_base;
+ struct clk *clk;
+ u32 duty_percent;
+};
+
+static inline void ep93xx_pwm_writel(struct ep93xx_pwm *pwm,
+ unsigned int val, unsigned int off)
+{
+ __raw_writel(val, pwm->mmio_base + off);
+}
+
+static inline unsigned int ep93xx_pwm_readl(struct ep93xx_pwm *pwm,
+ unsigned int off)
+{
+ return __raw_readl(pwm->mmio_base + off);
+}
+
+static inline void ep93xx_pwm_write_tc(struct ep93xx_pwm *pwm, u16 value)
+{
+ ep93xx_pwm_writel(pwm, value, EP93XX_PWMx_TERM_COUNT);
+}
+
+static inline u16 ep93xx_pwm_read_tc(struct ep93xx_pwm *pwm)
+{
+ return ep93xx_pwm_readl(pwm, EP93XX_PWMx_TERM_COUNT);
+}
+
+static inline void ep93xx_pwm_write_dc(struct ep93xx_pwm *pwm, u16 value)
+{
+ ep93xx_pwm_writel(pwm, value, EP93XX_PWMx_DUTY_CYCLE);
+}
+
+static inline void ep93xx_pwm_enable(struct ep93xx_pwm *pwm)
+{
+ ep93xx_pwm_writel(pwm, 0x1, EP93XX_PWMx_ENABLE);
+}
+
+static inline void ep93xx_pwm_disable(struct ep93xx_pwm *pwm)
+{
+ ep93xx_pwm_writel(pwm, 0x0, EP93XX_PWMx_ENABLE);
+}
+
+static inline int ep93xx_pwm_is_enabled(struct ep93xx_pwm *pwm)
+{
+ return ep93xx_pwm_readl(pwm, EP93XX_PWMx_ENABLE) & 0x1;
+}
+
+static inline void ep93xx_pwm_invert(struct ep93xx_pwm *pwm)
+{
+ ep93xx_pwm_writel(pwm, 0x1, EP93XX_PWMx_INVERT);
+}
+
+static inline void ep93xx_pwm_normal(struct ep93xx_pwm *pwm)
+{
+ ep93xx_pwm_writel(pwm, 0x0, EP93XX_PWMx_INVERT);
+}
+
+static inline int ep93xx_pwm_is_inverted(struct ep93xx_pwm *pwm)
+{
+ return ep93xx_pwm_readl(pwm, EP93XX_PWMx_INVERT) & 0x1;
+}
+
+/*
+ * /sys/devices/platform/ep93xx-pwm.N
+ * /min_freq read-only minimum pwm output frequency
+ * /max_req read-only maximum pwm output frequency
+ * /freq read-write pwm output frequency (0 = disable output)
+ * /duty_percent read-write pwm duty cycle percent (1..99)
+ * /invert read-write invert pwm output
+ */
+
+static ssize_t ep93xx_pwm_get_min_freq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
+ unsigned long rate = clk_get_rate(pwm->clk);
+
+ return sprintf(buf, "%ld\n", rate / (EP93XX_PWM_MAX_COUNT + 1));
+}
+
+static ssize_t ep93xx_pwm_get_max_freq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
+ unsigned long rate = clk_get_rate(pwm->clk);
+
+ return sprintf(buf, "%ld\n", rate / 2);
+}
+
+static ssize_t ep93xx_pwm_get_freq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
+
+ if (ep93xx_pwm_is_enabled(pwm)) {
+ unsigned long rate = clk_get_rate(pwm->clk);
+ u16 term = ep93xx_pwm_read_tc(pwm);
+
+ return sprintf(buf, "%ld\n", rate / (term + 1));
+ } else {
+ return sprintf(buf, "disabled\n");
+ }
+}
+
+static ssize_t ep93xx_pwm_set_freq(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
+ long val;
+ int err;
+
+ err = strict_strtol(buf, 10, &val);
+ if (err)
+ return -EINVAL;
+
+ if (val == 0) {
+ ep93xx_pwm_disable(pwm);
+ } else if (val <= (clk_get_rate(pwm->clk) / 2)) {
+ u32 term, duty;
+
+ val = (clk_get_rate(pwm->clk) / val) - 1;
+ if (val > EP93XX_PWM_MAX_COUNT)
+ val = EP93XX_PWM_MAX_COUNT;
+ if (val < 1)
+ val = 1;
+
+ term = ep93xx_pwm_read_tc(pwm);
+ duty = ((val + 1) * pwm->duty_percent / 100) - 1;
+
+ /* If pwm is running, order is important */
+ if (val > term) {
+ ep93xx_pwm_write_tc(pwm, val);
+ ep93xx_pwm_write_dc(pwm, duty);
+ } else {
+ ep93xx_pwm_write_dc(pwm, duty);
+ ep93xx_pwm_write_tc(pwm, val);
+ }
+
+ if (!ep93xx_pwm_is_enabled(pwm))
+ ep93xx_pwm_enable(pwm);
+ } else {
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t ep93xx_pwm_get_duty_percent(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", pwm->duty_percent);
+}
+
+static ssize_t ep93xx_pwm_set_duty_percent(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
+ long val;
+ int err;
+
+ err = strict_strtol(buf, 10, &val);
+ if (err)
+ return -EINVAL;
+
+ if (val > 0 && val < 100) {
+ u32 term = ep93xx_pwm_read_tc(pwm);
+ ep93xx_pwm_write_dc(pwm, ((term + 1) * val / 100) - 1);
+ pwm->duty_percent = val;
+ return count;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t ep93xx_pwm_get_invert(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d\n", ep93xx_pwm_is_inverted(pwm));
+}
+
+static ssize_t ep93xx_pwm_set_invert(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
+ long val;
+ int err;
+
+ err = strict_strtol(buf, 10, &val);
+ if (err)
+ return -EINVAL;
+
+ if (val == 0)
+ ep93xx_pwm_normal(pwm);
+ else if (val == 1)
+ ep93xx_pwm_invert(pwm);
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+static DEVICE_ATTR(min_freq, S_IRUGO, ep93xx_pwm_get_min_freq, NULL);
+static DEVICE_ATTR(max_freq, S_IRUGO, ep93xx_pwm_get_max_freq, NULL);
+static DEVICE_ATTR(freq, S_IWUGO | S_IRUGO,
+ ep93xx_pwm_get_freq, ep93xx_pwm_set_freq);
+static DEVICE_ATTR(duty_percent, S_IWUGO | S_IRUGO,
+ ep93xx_pwm_get_duty_percent, ep93xx_pwm_set_duty_percent);
+static DEVICE_ATTR(invert, S_IWUGO | S_IRUGO,
+ ep93xx_pwm_get_invert, ep93xx_pwm_set_invert);
+
+static struct attribute *ep93xx_pwm_attrs[] = {
+ &dev_attr_min_freq.attr,
+ &dev_attr_max_freq.attr,
+ &dev_attr_freq.attr,
+ &dev_attr_duty_percent.attr,
+ &dev_attr_invert.attr,
+ NULL
+};
+
+static const struct attribute_group ep93xx_pwm_sysfs_files = {
+ .attrs = ep93xx_pwm_attrs,
+};
+
+static int __init ep93xx_pwm_probe(struct platform_device *pdev)
+{
+ struct ep93xx_pwm *pwm;
+ struct resource *res;
+ int err;
+
+ err = ep93xx_pwm_acquire_gpio(pdev);
+ if (err)
+ return err;
+
+ pwm = kzalloc(sizeof(struct ep93xx_pwm), GFP_KERNEL);
+ if (!pwm) {
+ err = -ENOMEM;
+ goto fail_no_mem;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ err = -ENXIO;
+ goto fail_no_mem_resource;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (res == NULL) {
+ err = -EBUSY;
+ goto fail_no_mem_resource;
+ }
+
+ pwm->mmio_base = ioremap(res->start, resource_size(res));
+ if (pwm->mmio_base == NULL) {
+ err = -ENXIO;
+ goto fail_no_ioremap;
+ }
+
+ err = sysfs_create_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files);
+ if (err)
+ goto fail_no_sysfs;
+
+ pwm->clk = clk_get(&pdev->dev, "pwm_clk");
+ if (IS_ERR(pwm->clk)) {
+ err = PTR_ERR(pwm->clk);
+ goto fail_no_clk;
+ }
+
+ pwm->duty_percent = 50;
+
+ platform_set_drvdata(pdev, pwm);
+
+ /* disable pwm at startup. Avoids zero value. */
+ ep93xx_pwm_disable(pwm);
+ ep93xx_pwm_write_tc(pwm, EP93XX_PWM_MAX_COUNT);
+ ep93xx_pwm_write_dc(pwm, EP93XX_PWM_MAX_COUNT / 2);
+
+ clk_enable(pwm->clk);
+
+ return 0;
+
+fail_no_clk:
+ sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files);
+fail_no_sysfs:
+ iounmap(pwm->mmio_base);
+fail_no_ioremap:
+ release_mem_region(res->start, resource_size(res));
+fail_no_mem_resource:
+ kfree(pwm);
+fail_no_mem:
+ ep93xx_pwm_release_gpio(pdev);
+ return err;
+}
+
+static int __exit ep93xx_pwm_remove(struct platform_device *pdev)
+{
+ struct ep93xx_pwm *pwm = platform_get_drvdata(pdev);
+ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ ep93xx_pwm_disable(pwm);
+ clk_disable(pwm->clk);
+ clk_put(pwm->clk);
+ platform_set_drvdata(pdev, NULL);
+ sysfs_remove_group(&pdev->dev.kobj, &ep93xx_pwm_sysfs_files);
+ iounmap(pwm->mmio_base);
+ release_mem_region(res->start, resource_size(res));
+ kfree(pwm);
+ ep93xx_pwm_release_gpio(pdev);
+
+ return 0;
+}
+
+static struct platform_driver ep93xx_pwm_driver = {
+ .driver = {
+ .name = "ep93xx-pwm",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(ep93xx_pwm_remove),
+};
+
+static int __init ep93xx_pwm_init(void)
+{
+ return platform_driver_probe(&ep93xx_pwm_driver, ep93xx_pwm_probe);
+}
+
+static void __exit ep93xx_pwm_exit(void)
+{
+ platform_driver_unregister(&ep93xx_pwm_driver);
+}
+
+module_init(ep93xx_pwm_init);
+module_exit(ep93xx_pwm_exit);
+
+MODULE_AUTHOR("Matthieu Crapet <mcrapet@gmail.com>, "
+ "H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_DESCRIPTION("EP93xx PWM driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ep93xx-pwm");
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index e1aa8471ab1..8741d0f5146 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -21,6 +21,7 @@
#include <linux/amba/bus.h>
#include <linux/clk.h>
#include <linux/scatterlist.h>
+#include <linux/gpio.h>
#include <asm/cacheflush.h>
#include <asm/div64.h>
@@ -430,7 +431,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
clk = 255;
host->cclk = host->mclk / (2 * (clk + 1));
}
- if (host->hw_designer == 0x80)
+ if (host->hw_designer == AMBA_VENDOR_ST)
clk |= MCI_FCEN; /* Bug fix in ST IP block */
clk |= MCI_CLK_ENABLE;
}
@@ -443,7 +444,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
break;
case MMC_POWER_UP:
/* The ST version does not have this, fall through to POWER_ON */
- if (host->hw_designer != 0x80) {
+ if (host->hw_designer != AMBA_VENDOR_ST) {
pwr |= MCI_PWR_UP;
break;
}
@@ -453,7 +454,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
- if (host->hw_designer != 0x80)
+ if (host->hw_designer != AMBA_VENDOR_ST)
pwr |= MCI_ROD;
else {
/*
@@ -472,17 +473,41 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
}
}
+static int mmci_get_ro(struct mmc_host *mmc)
+{
+ struct mmci_host *host = mmc_priv(mmc);
+
+ if (host->gpio_wp == -ENOSYS)
+ return -ENOSYS;
+
+ return gpio_get_value(host->gpio_wp);
+}
+
+static int mmci_get_cd(struct mmc_host *mmc)
+{
+ struct mmci_host *host = mmc_priv(mmc);
+ unsigned int status;
+
+ if (host->gpio_cd == -ENOSYS)
+ status = host->plat->status(mmc_dev(host->mmc));
+ else
+ status = gpio_get_value(host->gpio_cd);
+
+ return !status;
+}
+
static const struct mmc_host_ops mmci_ops = {
.request = mmci_request,
.set_ios = mmci_set_ios,
+ .get_ro = mmci_get_ro,
+ .get_cd = mmci_get_cd,
};
static void mmci_check_status(unsigned long data)
{
struct mmci_host *host = (struct mmci_host *)data;
- unsigned int status;
+ unsigned int status = mmci_get_cd(host->mmc);
- status = host->plat->status(mmc_dev(host->mmc));
if (status ^ host->oldstat)
mmc_detect_change(host->mmc, 0);
@@ -515,12 +540,15 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
host = mmc_priv(mmc);
host->mmc = mmc;
- /* Bits 12 thru 19 is the designer */
- host->hw_designer = (dev->periphid >> 12) & 0xff;
- /* Bits 20 thru 23 is the revison */
- host->hw_revision = (dev->periphid >> 20) & 0xf;
+
+ host->gpio_wp = -ENOSYS;
+ host->gpio_cd = -ENOSYS;
+
+ host->hw_designer = amba_manf(dev);
+ host->hw_revision = amba_rev(dev);
DBG(host, "designer ID = 0x%02x\n", host->hw_designer);
DBG(host, "revision = 0x%01x\n", host->hw_revision);
+
host->clk = clk_get(&dev->dev, NULL);
if (IS_ERR(host->clk)) {
ret = PTR_ERR(host->clk);
@@ -591,6 +619,27 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
writel(0, host->base + MMCIMASK1);
writel(0xfff, host->base + MMCICLEAR);
+#ifdef CONFIG_GPIOLIB
+ if (gpio_is_valid(plat->gpio_cd)) {
+ ret = gpio_request(plat->gpio_cd, DRIVER_NAME " (cd)");
+ if (ret == 0)
+ ret = gpio_direction_input(plat->gpio_cd);
+ if (ret == 0)
+ host->gpio_cd = plat->gpio_cd;
+ else if (ret != -ENOSYS)
+ goto err_gpio_cd;
+ }
+ if (gpio_is_valid(plat->gpio_wp)) {
+ ret = gpio_request(plat->gpio_wp, DRIVER_NAME " (wp)");
+ if (ret == 0)
+ ret = gpio_direction_input(plat->gpio_wp);
+ if (ret == 0)
+ host->gpio_wp = plat->gpio_wp;
+ else if (ret != -ENOSYS)
+ goto err_gpio_wp;
+ }
+#endif
+
ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);
if (ret)
goto unmap;
@@ -602,6 +651,7 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
writel(MCI_IRQENABLE, host->base + MMCIMASK0);
amba_set_drvdata(dev, mmc);
+ host->oldstat = mmci_get_cd(host->mmc);
mmc_add_host(mmc);
@@ -620,6 +670,12 @@ static int __devinit mmci_probe(struct amba_device *dev, struct amba_id *id)
irq0_free:
free_irq(dev->irq[0], host);
unmap:
+ if (host->gpio_wp != -ENOSYS)
+ gpio_free(host->gpio_wp);
+ err_gpio_wp:
+ if (host->gpio_cd != -ENOSYS)
+ gpio_free(host->gpio_cd);
+ err_gpio_cd:
iounmap(host->base);
clk_disable:
clk_disable(host->clk);
@@ -655,6 +711,11 @@ static int __devexit mmci_remove(struct amba_device *dev)
free_irq(dev->irq[0], host);
free_irq(dev->irq[1], host);
+ if (host->gpio_wp != -ENOSYS)
+ gpio_free(host->gpio_wp);
+ if (host->gpio_cd != -ENOSYS)
+ gpio_free(host->gpio_cd);
+
iounmap(host->base);
clk_disable(host->clk);
clk_put(host->clk);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 0441bac1c0e..839f264c972 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -151,6 +151,8 @@ struct mmci_host {
struct mmc_data *data;
struct mmc_host *mmc;
struct clk *clk;
+ int gpio_cd;
+ int gpio_wp;
unsigned int data_xfered;
diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c
index 2c410a01131..0f5562aeedc 100644
--- a/drivers/mtd/nand/ts7250.c
+++ b/drivers/mtd/nand/ts7250.c
@@ -24,8 +24,11 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
-#include <asm/io.h>
+#include <linux/io.h>
+
#include <mach/hardware.h>
+#include <mach/ts72xx.h>
+
#include <asm/sizes.h>
#include <asm/mach-types.h>
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 21333c18f34..507569a4f23 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -209,7 +209,7 @@ config MII
config MACB
tristate "Atmel MACB support"
- depends on AVR32 || ARCH_AT91SAM9260 || ARCH_AT91SAM9263 || ARCH_AT91SAM9G20 || ARCH_AT91CAP9
+ depends on AVR32 || ARCH_AT91SAM9260 || ARCH_AT91SAM9263 || ARCH_AT91SAM9G20 || ARCH_AT91SAM9G45 || ARCH_AT91CAP9
select PHYLIB
help
The Atmel MACB ethernet interface is found on many AT32 and AT91
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 2ccbd185a5f..1be6bf7e8ce 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -42,6 +42,12 @@ static char *init_device;
module_param_named(device, init_device, charp, 0400);
MODULE_PARM_DESC(device, "specify initial device");
+static struct kmem_cache *zfcp_cache_hw_align(const char *name,
+ unsigned long size)
+{
+ return kmem_cache_create(name, size, roundup_pow_of_two(size), 0, NULL);
+}
+
static int zfcp_reqlist_alloc(struct zfcp_adapter *adapter)
{
int idx;
@@ -78,7 +84,7 @@ static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun)
struct zfcp_port *port;
struct zfcp_unit *unit;
- down(&zfcp_data.config_sema);
+ mutex_lock(&zfcp_data.config_mutex);
read_lock_irq(&zfcp_data.config_lock);
adapter = zfcp_get_adapter_by_busid(busid);
if (adapter)
@@ -93,31 +99,23 @@ static void __init zfcp_init_device_configure(char *busid, u64 wwpn, u64 lun)
unit = zfcp_unit_enqueue(port, lun);
if (IS_ERR(unit))
goto out_unit;
- up(&zfcp_data.config_sema);
+ mutex_unlock(&zfcp_data.config_mutex);
ccw_device_set_online(adapter->ccw_device);
zfcp_erp_wait(adapter);
flush_work(&unit->scsi_work);
- down(&zfcp_data.config_sema);
+ mutex_lock(&zfcp_data.config_mutex);
zfcp_unit_put(unit);
out_unit:
zfcp_port_put(port);
out_port:
zfcp_adapter_put(adapter);
out_adapter:
- up(&zfcp_data.config_sema);
+ mutex_unlock(&zfcp_data.config_mutex);
return;
}
-static struct kmem_cache *zfcp_cache_create(int size, char *name)
-{
- int align = 1;
- while ((size - align) > 0)
- align <<= 1;
- return kmem_cache_create(name , size, align, 0, NULL);
-}
-
static void __init zfcp_init_device_setup(char *devstr)
{
char *token;
@@ -158,24 +156,27 @@ static int __init zfcp_module_init(void)
{
int retval = -ENOMEM;
- zfcp_data.fsf_req_qtcb_cache = zfcp_cache_create(
- sizeof(struct zfcp_fsf_req_qtcb), "zfcp_fsf");
- if (!zfcp_data.fsf_req_qtcb_cache)
+ zfcp_data.gpn_ft_cache = zfcp_cache_hw_align("zfcp_gpn",
+ sizeof(struct ct_iu_gpn_ft_req));
+ if (!zfcp_data.gpn_ft_cache)
goto out;
- zfcp_data.sr_buffer_cache = zfcp_cache_create(
- sizeof(struct fsf_status_read_buffer), "zfcp_sr");
+ zfcp_data.qtcb_cache = zfcp_cache_hw_align("zfcp_qtcb",
+ sizeof(struct fsf_qtcb));
+ if (!zfcp_data.qtcb_cache)
+ goto out_qtcb_cache;
+
+ zfcp_data.sr_buffer_cache = zfcp_cache_hw_align("zfcp_sr",
+ sizeof(struct fsf_status_read_buffer));
if (!zfcp_data.sr_buffer_cache)
goto out_sr_cache;
- zfcp_data.gid_pn_cache = zfcp_cache_create(
- sizeof(struct zfcp_gid_pn_data), "zfcp_gid");
+ zfcp_data.gid_pn_cache = zfcp_cache_hw_align("zfcp_gid",
+ sizeof(struct zfcp_gid_pn_data));
if (!zfcp_data.gid_pn_cache)
goto out_gid_cache;
- zfcp_data.work_queue = create_singlethread_workqueue("zfcp_wq");
-
- sema_init(&zfcp_data.config_sema, 1);
+ mutex_init(&zfcp_data.config_mutex);
rwlock_init(&zfcp_data.config_lock);
zfcp_data.scsi_transport_template =
@@ -209,7 +210,9 @@ out_transport:
out_gid_cache:
kmem_cache_destroy(zfcp_data.sr_buffer_cache);
out_sr_cache:
- kmem_cache_destroy(zfcp_data.fsf_req_qtcb_cache);
+ kmem_cache_destroy(zfcp_data.qtcb_cache);
+out_qtcb_cache:
+ kmem_cache_destroy(zfcp_data.gpn_ft_cache);
out:
return retval;
}
@@ -263,7 +266,7 @@ static void zfcp_sysfs_unit_release(struct device *dev)
* @port: pointer to port where unit is added
* @fcp_lun: FCP LUN of unit to be enqueued
* Returns: pointer to enqueued unit on success, ERR_PTR on error
- * Locks: config_sema must be held to serialize changes to the unit list
+ * Locks: config_mutex must be held to serialize changes to the unit list
*
* Sets up some unit internal structures and creates sysfs entry.
*/
@@ -271,6 +274,13 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
{
struct zfcp_unit *unit;
+ read_lock_irq(&zfcp_data.config_lock);
+ if (zfcp_get_unit_by_lun(port, fcp_lun)) {
+ read_unlock_irq(&zfcp_data.config_lock);
+ return ERR_PTR(-EINVAL);
+ }
+ read_unlock_irq(&zfcp_data.config_lock);
+
unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL);
if (!unit)
return ERR_PTR(-ENOMEM);
@@ -282,8 +292,11 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
unit->port = port;
unit->fcp_lun = fcp_lun;
- dev_set_name(&unit->sysfs_device, "0x%016llx",
- (unsigned long long) fcp_lun);
+ if (dev_set_name(&unit->sysfs_device, "0x%016llx",
+ (unsigned long long) fcp_lun)) {
+ kfree(unit);
+ return ERR_PTR(-ENOMEM);
+ }
unit->sysfs_device.parent = &port->sysfs_device;
unit->sysfs_device.release = zfcp_sysfs_unit_release;
dev_set_drvdata(&unit->sysfs_device, unit);
@@ -299,20 +312,15 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
unit->latencies.cmd.channel.min = 0xFFFFFFFF;
unit->latencies.cmd.fabric.min = 0xFFFFFFFF;
- read_lock_irq(&zfcp_data.config_lock);
- if (zfcp_get_unit_by_lun(port, fcp_lun)) {
- read_unlock_irq(&zfcp_data.config_lock);
- goto err_out_free;
+ if (device_register(&unit->sysfs_device)) {
+ put_device(&unit->sysfs_device);
+ return ERR_PTR(-EINVAL);
}
- read_unlock_irq(&zfcp_data.config_lock);
-
- if (device_register(&unit->sysfs_device))
- goto err_out_free;
if (sysfs_create_group(&unit->sysfs_device.kobj,
&zfcp_sysfs_unit_attrs)) {
device_unregister(&unit->sysfs_device);
- return ERR_PTR(-EIO);
+ return ERR_PTR(-EINVAL);
}
zfcp_unit_get(unit);
@@ -327,10 +335,6 @@ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun)
zfcp_port_get(port);
return unit;
-
-err_out_free:
- kfree(unit);
- return ERR_PTR(-EINVAL);
}
/**
@@ -353,37 +357,47 @@ void zfcp_unit_dequeue(struct zfcp_unit *unit)
static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
{
- /* must only be called with zfcp_data.config_sema taken */
- adapter->pool.fsf_req_erp =
- mempool_create_slab_pool(1, zfcp_data.fsf_req_qtcb_cache);
- if (!adapter->pool.fsf_req_erp)
+ /* must only be called with zfcp_data.config_mutex taken */
+ adapter->pool.erp_req =
+ mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req));
+ if (!adapter->pool.erp_req)
+ return -ENOMEM;
+
+ adapter->pool.gid_pn_req =
+ mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req));
+ if (!adapter->pool.gid_pn_req)
return -ENOMEM;
- adapter->pool.fsf_req_scsi =
- mempool_create_slab_pool(1, zfcp_data.fsf_req_qtcb_cache);
- if (!adapter->pool.fsf_req_scsi)
+ adapter->pool.scsi_req =
+ mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req));
+ if (!adapter->pool.scsi_req)
return -ENOMEM;
- adapter->pool.fsf_req_abort =
- mempool_create_slab_pool(1, zfcp_data.fsf_req_qtcb_cache);
- if (!adapter->pool.fsf_req_abort)
+ adapter->pool.scsi_abort =
+ mempool_create_kmalloc_pool(1, sizeof(struct zfcp_fsf_req));
+ if (!adapter->pool.scsi_abort)
return -ENOMEM;
- adapter->pool.fsf_req_status_read =
+ adapter->pool.status_read_req =
mempool_create_kmalloc_pool(FSF_STATUS_READS_RECOM,
sizeof(struct zfcp_fsf_req));
- if (!adapter->pool.fsf_req_status_read)
+ if (!adapter->pool.status_read_req)
return -ENOMEM;
- adapter->pool.data_status_read =
+ adapter->pool.qtcb_pool =
+ mempool_create_slab_pool(4, zfcp_data.qtcb_cache);
+ if (!adapter->pool.qtcb_pool)
+ return -ENOMEM;
+
+ adapter->pool.status_read_data =
mempool_create_slab_pool(FSF_STATUS_READS_RECOM,
zfcp_data.sr_buffer_cache);
- if (!adapter->pool.data_status_read)
+ if (!adapter->pool.status_read_data)
return -ENOMEM;
- adapter->pool.data_gid_pn =
+ adapter->pool.gid_pn_data =
mempool_create_slab_pool(1, zfcp_data.gid_pn_cache);
- if (!adapter->pool.data_gid_pn)
+ if (!adapter->pool.gid_pn_data)
return -ENOMEM;
return 0;
@@ -391,19 +405,21 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter)
static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
{
- /* zfcp_data.config_sema must be held */
- if (adapter->pool.fsf_req_erp)
- mempool_destroy(adapter->pool.fsf_req_erp);
- if (adapter->pool.fsf_req_scsi)
- mempool_destroy(adapter->pool.fsf_req_scsi);
- if (adapter->pool.fsf_req_abort)
- mempool_destroy(adapter->pool.fsf_req_abort);
- if (adapter->pool.fsf_req_status_read)
- mempool_destroy(adapter->pool.fsf_req_status_read);
- if (adapter->pool.data_status_read)
- mempool_destroy(adapter->pool.data_status_read);
- if (adapter->pool.data_gid_pn)
- mempool_destroy(adapter->pool.data_gid_pn);
+ /* zfcp_data.config_mutex must be held */
+ if (adapter->pool.erp_req)
+ mempool_destroy(adapter->pool.erp_req);
+ if (adapter->pool.scsi_req)
+ mempool_destroy(adapter->pool.scsi_req);
+ if (adapter->pool.scsi_abort)
+ mempool_destroy(adapter->pool.scsi_abort);
+ if (adapter->pool.qtcb_pool)
+ mempool_destroy(adapter->pool.qtcb_pool);
+ if (adapter->pool.status_read_req)
+ mempool_destroy(adapter->pool.status_read_req);
+ if (adapter->pool.status_read_data)
+ mempool_destroy(adapter->pool.status_read_data);
+ if (adapter->pool.gid_pn_data)
+ mempool_destroy(adapter->pool.gid_pn_data);
}
/**
@@ -418,7 +434,7 @@ static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter)
int zfcp_status_read_refill(struct zfcp_adapter *adapter)
{
while (atomic_read(&adapter->stat_miss) > 0)
- if (zfcp_fsf_status_read(adapter)) {
+ if (zfcp_fsf_status_read(adapter->qdio)) {
if (atomic_read(&adapter->stat_miss) >= 16) {
zfcp_erp_adapter_reopen(adapter, 0, "axsref1",
NULL);
@@ -446,6 +462,27 @@ static void zfcp_print_sl(struct seq_file *m, struct service_level *sl)
adapter->fsf_lic_version);
}
+static int zfcp_setup_adapter_work_queue(struct zfcp_adapter *adapter)
+{
+ char name[TASK_COMM_LEN];
+
+ snprintf(name, sizeof(name), "zfcp_q_%s",
+ dev_name(&adapter->ccw_device->dev));
+ adapter->work_queue = create_singlethread_workqueue(name);
+
+ if (adapter->work_queue)
+ return 0;
+ return -ENOMEM;
+}
+
+static void zfcp_destroy_adapter_work_queue(struct zfcp_adapter *adapter)
+{
+ if (adapter->work_queue)
+ destroy_workqueue(adapter->work_queue);
+ adapter->work_queue = NULL;
+
+}
+
/**
* zfcp_adapter_enqueue - enqueue a new adapter to the list
* @ccw_device: pointer to the struct cc_device
@@ -455,7 +492,7 @@ static void zfcp_print_sl(struct seq_file *m, struct service_level *sl)
* Enqueues an adapter at the end of the adapter list in the driver data.
* All adapter internal structures are set up.
* Proc-fs entries are also created.
- * locks: config_sema must be held to serialise changes to the adapter list
+ * locks: config_mutex must be held to serialize changes to the adapter list
*/
int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
{
@@ -463,37 +500,37 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
/*
* Note: It is safe to release the list_lock, as any list changes
- * are protected by the config_sema, which must be held to get here
+ * are protected by the config_mutex, which must be held to get here
*/
adapter = kzalloc(sizeof(struct zfcp_adapter), GFP_KERNEL);
if (!adapter)
return -ENOMEM;
- adapter->gs = kzalloc(sizeof(struct zfcp_wka_ports), GFP_KERNEL);
- if (!adapter->gs) {
- kfree(adapter);
- return -ENOMEM;
- }
-
ccw_device->handler = NULL;
adapter->ccw_device = ccw_device;
atomic_set(&adapter->refcount, 0);
- if (zfcp_qdio_allocate(adapter))
- goto qdio_allocate_failed;
+ if (zfcp_qdio_setup(adapter))
+ goto qdio_failed;
if (zfcp_allocate_low_mem_buffers(adapter))
- goto failed_low_mem_buffers;
+ goto low_mem_buffers_failed;
if (zfcp_reqlist_alloc(adapter))
- goto failed_low_mem_buffers;
+ goto low_mem_buffers_failed;
- if (zfcp_adapter_debug_register(adapter))
+ if (zfcp_dbf_adapter_register(adapter))
goto debug_register_failed;
+ if (zfcp_setup_adapter_work_queue(adapter))
+ goto work_queue_failed;
+
+ if (zfcp_fc_gs_setup(adapter))
+ goto generic_services_failed;
+
init_waitqueue_head(&adapter->remove_wq);
- init_waitqueue_head(&adapter->erp_thread_wqh);
+ init_waitqueue_head(&adapter->erp_ready_wq);
init_waitqueue_head(&adapter->erp_done_wqh);
INIT_LIST_HEAD(&adapter->port_list_head);
@@ -502,20 +539,14 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
spin_lock_init(&adapter->req_list_lock);
- spin_lock_init(&adapter->hba_dbf_lock);
- spin_lock_init(&adapter->san_dbf_lock);
- spin_lock_init(&adapter->scsi_dbf_lock);
- spin_lock_init(&adapter->rec_dbf_lock);
- spin_lock_init(&adapter->req_q_lock);
- spin_lock_init(&adapter->qdio_stat_lock);
-
rwlock_init(&adapter->erp_lock);
rwlock_init(&adapter->abort_lock);
- sema_init(&adapter->erp_ready_sem, 0);
+ if (zfcp_erp_thread_setup(adapter))
+ goto erp_thread_failed;
INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
- INIT_WORK(&adapter->scan_work, _zfcp_scan_ports_later);
+ INIT_WORK(&adapter->scan_work, _zfcp_fc_scan_ports_later);
adapter->service_level.seq_print = zfcp_print_sl;
@@ -529,20 +560,25 @@ int zfcp_adapter_enqueue(struct ccw_device *ccw_device)
goto sysfs_failed;
atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
- zfcp_fc_wka_ports_init(adapter);
if (!zfcp_adapter_scsi_register(adapter))
return 0;
sysfs_failed:
- zfcp_adapter_debug_unregister(adapter);
+ zfcp_erp_thread_kill(adapter);
+erp_thread_failed:
+ zfcp_fc_gs_destroy(adapter);
+generic_services_failed:
+ zfcp_destroy_adapter_work_queue(adapter);
+work_queue_failed:
+ zfcp_dbf_adapter_unregister(adapter->dbf);
debug_register_failed:
dev_set_drvdata(&ccw_device->dev, NULL);
kfree(adapter->req_list);
-failed_low_mem_buffers:
+low_mem_buffers_failed:
zfcp_free_low_mem_buffers(adapter);
-qdio_allocate_failed:
- zfcp_qdio_free(adapter);
+qdio_failed:
+ zfcp_qdio_destroy(adapter->qdio);
kfree(adapter);
return -ENOMEM;
}
@@ -559,6 +595,7 @@ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
cancel_work_sync(&adapter->scan_work);
cancel_work_sync(&adapter->stat_work);
+ zfcp_fc_wka_ports_force_offline(adapter->gs);
zfcp_adapter_scsi_unregister(adapter);
sysfs_remove_group(&adapter->ccw_device->dev.kobj,
&zfcp_sysfs_adapter_attrs);
@@ -570,13 +607,15 @@ void zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
if (!retval)
return;
- zfcp_adapter_debug_unregister(adapter);
- zfcp_qdio_free(adapter);
+ zfcp_fc_gs_destroy(adapter);
+ zfcp_erp_thread_kill(adapter);
+ zfcp_destroy_adapter_work_queue(adapter);
+ zfcp_dbf_adapter_unregister(adapter->dbf);
zfcp_free_low_mem_buffers(adapter);
+ zfcp_qdio_destroy(adapter->qdio);
kfree(adapter->req_list);
kfree(adapter->fc_stats);
kfree(adapter->stats_reset_data);
- kfree(adapter->gs);
kfree(adapter);
}
@@ -592,7 +631,7 @@ static void zfcp_sysfs_port_release(struct device *dev)
* @status: initial status for the port
* @d_id: destination id of the remote port to be enqueued
* Returns: pointer to enqueued port on success, ERR_PTR on error
- * Locks: config_sema must be held to serialize changes to the port list
+ * Locks: config_mutex must be held to serialize changes to the port list
*
* All port internal structures are set up and the sysfs entry is generated.
* d_id is used to enqueue ports with a well known address like the Directory
@@ -602,7 +641,13 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
u32 status, u32 d_id)
{
struct zfcp_port *port;
- int retval;
+
+ read_lock_irq(&zfcp_data.config_lock);
+ if (zfcp_get_port_by_wwpn(adapter, wwpn)) {
+ read_unlock_irq(&zfcp_data.config_lock);
+ return ERR_PTR(-EINVAL);
+ }
+ read_unlock_irq(&zfcp_data.config_lock);
port = kzalloc(sizeof(struct zfcp_port), GFP_KERNEL);
if (!port)
@@ -610,7 +655,7 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
init_waitqueue_head(&port->remove_wq);
INIT_LIST_HEAD(&port->unit_list_head);
- INIT_WORK(&port->gid_pn_work, zfcp_erp_port_strategy_open_lookup);
+ INIT_WORK(&port->gid_pn_work, zfcp_fc_port_did_lookup);
INIT_WORK(&port->test_link_work, zfcp_fc_link_test_work);
INIT_WORK(&port->rport_work, zfcp_scsi_rport_work);
@@ -623,29 +668,24 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status);
atomic_set(&port->refcount, 0);
- dev_set_name(&port->sysfs_device, "0x%016llx",
- (unsigned long long)wwpn);
+ if (dev_set_name(&port->sysfs_device, "0x%016llx",
+ (unsigned long long)wwpn)) {
+ kfree(port);
+ return ERR_PTR(-ENOMEM);
+ }
port->sysfs_device.parent = &adapter->ccw_device->dev;
-
port->sysfs_device.release = zfcp_sysfs_port_release;
dev_set_drvdata(&port->sysfs_device, port);
- read_lock_irq(&zfcp_data.config_lock);
- if (zfcp_get_port_by_wwpn(adapter, wwpn)) {
- read_unlock_irq(&zfcp_data.config_lock);
- goto err_out_free;
+ if (device_register(&port->sysfs_device)) {
+ put_device(&port->sysfs_device);
+ return ERR_PTR(-EINVAL);
}
- read_unlock_irq(&zfcp_data.config_lock);
- if (device_register(&port->sysfs_device))
- goto err_out_free;
-
- retval = sysfs_create_group(&port->sysfs_device.kobj,
- &zfcp_sysfs_port_attrs);
-
- if (retval) {
+ if (sysfs_create_group(&port->sysfs_device.kobj,
+ &zfcp_sysfs_port_attrs)) {
device_unregister(&port->sysfs_device);
- goto err_out;
+ return ERR_PTR(-EINVAL);
}
zfcp_port_get(port);
@@ -659,11 +699,6 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn,
zfcp_adapter_get(adapter);
return port;
-
-err_out_free:
- kfree(port);
-err_out:
- return ERR_PTR(-EINVAL);
}
/**
@@ -672,12 +707,11 @@ err_out:
*/
void zfcp_port_dequeue(struct zfcp_port *port)
{
- wait_event(port->remove_wq, atomic_read(&port->refcount) == 0);
write_lock_irq(&zfcp_data.config_lock);
list_del(&port->list);
write_unlock_irq(&zfcp_data.config_lock);
- if (port->rport)
- port->rport->dd_data = NULL;
+ wait_event(port->remove_wq, atomic_read(&port->refcount) == 0);
+ cancel_work_sync(&port->rport_work); /* usually not necessary */
zfcp_adapter_put(port->adapter);
sysfs_remove_group(&port->sysfs_device.kobj, &zfcp_sysfs_port_attrs);
device_unregister(&port->sysfs_device);
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index d9da5c42ccb..0c90f8e7160 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -18,12 +18,15 @@ static int zfcp_ccw_suspend(struct ccw_device *cdev)
{
struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev);
- down(&zfcp_data.config_sema);
+ if (!adapter)
+ return 0;
+
+ mutex_lock(&zfcp_data.config_mutex);
zfcp_erp_adapter_shutdown(adapter, 0, "ccsusp1", NULL);
zfcp_erp_wait(adapter);
- up(&zfcp_data.config_sema);
+ mutex_unlock(&zfcp_data.config_mutex);
return 0;
}
@@ -33,6 +36,9 @@ static int zfcp_ccw_activate(struct ccw_device *cdev)
{
struct zfcp_adapter *adapter = dev_get_drvdata(&cdev->dev);
+ if (!adapter)
+ return 0;
+
zfcp_erp_modify_adapter_status(adapter, "ccresu1", NULL,
ZFCP_STATUS_COMMON_RUNNING, ZFCP_SET);
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
@@ -63,25 +69,14 @@ int zfcp_ccw_priv_sch(struct zfcp_adapter *adapter)
* zfcp_ccw_probe - probe function of zfcp driver
* @ccw_device: pointer to belonging ccw device
*
- * This function gets called by the common i/o layer and sets up the initial
- * data structures for each fcp adapter, which was detected by the system.
- * Also the sysfs files for this adapter will be created by this function.
- * In addition the nameserver port will be added to the ports of the adapter
- * and its sysfs representation will be created too.
+ * This function gets called by the common i/o layer for each FCP
+ * device found on the current system. This is only a stub to make cio
+ * work: To only allocate adapter resources for devices actually used,
+ * the allocation is deferred to the first call to ccw_set_online.
*/
static int zfcp_ccw_probe(struct ccw_device *ccw_device)
{
- int retval = 0;
-
- down(&zfcp_data.config_sema);
- if (zfcp_adapter_enqueue(ccw_device)) {
- dev_err(&ccw_device->dev,
- "Setting up data structures for the "
- "FCP adapter failed\n");
- retval = -EINVAL;
- }
- up(&zfcp_data.config_sema);
- return retval;
+ return 0;
}
/**
@@ -102,8 +97,11 @@ static void zfcp_ccw_remove(struct ccw_device *ccw_device)
LIST_HEAD(port_remove_lh);
ccw_device_set_offline(ccw_device);
- down(&zfcp_data.config_sema);
+
+ mutex_lock(&zfcp_data.config_mutex);
adapter = dev_get_drvdata(&ccw_device->dev);
+ if (!adapter)
+ goto out;
write_lock_irq(&zfcp_data.config_lock);
list_for_each_entry_safe(port, p, &adapter->port_list_head, list) {
@@ -129,29 +127,41 @@ static void zfcp_ccw_remove(struct ccw_device *ccw_device)
wait_event(adapter->remove_wq, atomic_read(&adapter->refcount) == 0);
zfcp_adapter_dequeue(adapter);
- up(&zfcp_data.config_sema);
+out:
+ mutex_unlock(&zfcp_data.config_mutex);
}
/**
* zfcp_ccw_set_online - set_online function of zfcp driver
* @ccw_device: pointer to belonging ccw device
*
- * This function gets called by the common i/o layer and sets an adapter
- * into state online. Setting an fcp device online means that it will be
- * registered with the SCSI stack, that the QDIO queues will be set up
- * and that the adapter will be opened (asynchronously).
+ * This function gets called by the common i/o layer and sets an
+ * adapter into state online. The first call will allocate all
+ * adapter resources that will be retained until the device is removed
+ * via zfcp_ccw_remove.
+ *
+ * Setting an fcp device online means that it will be registered with
+ * the SCSI stack, that the QDIO queues will be set up and that the
+ * adapter will be opened.
*/
static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
- int retval;
+ int ret = 0;
- down(&zfcp_data.config_sema);
+ mutex_lock(&zfcp_data.config_mutex);
adapter = dev_get_drvdata(&ccw_device->dev);
- retval = zfcp_erp_thread_setup(adapter);
- if (retval)
- goto out;
+ if (!adapter) {
+ ret = zfcp_adapter_enqueue(ccw_device);
+ if (ret) {
+ dev_err(&ccw_device->dev,
+ "Setting up data structures for the "
+ "FCP adapter failed\n");
+ goto out;
+ }
+ adapter = dev_get_drvdata(&ccw_device->dev);
+ }
/* initialize request counter */
BUG_ON(!zfcp_reqlist_isempty(adapter));
@@ -162,13 +172,11 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device)
zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
"ccsonl2", NULL);
zfcp_erp_wait(adapter);
- up(&zfcp_data.config_sema);
- flush_work(&adapter->scan_work);
- return 0;
-
- out:
- up(&zfcp_data.config_sema);
- return retval;
+out:
+ mutex_unlock(&zfcp_data.config_mutex);
+ if (!ret)
+ flush_work(&adapter->scan_work);
+ return ret;
}
/**
@@ -182,12 +190,15 @@ static int zfcp_ccw_set_offline(struct ccw_device *ccw_device)
{
struct zfcp_adapter *adapter;
- down(&zfcp_data.config_sema);
+ mutex_lock(&zfcp_data.config_mutex);
adapter = dev_get_drvdata(&ccw_device->dev);
+ if (!adapter)
+ goto out;
+
zfcp_erp_adapter_shutdown(adapter, 0, "ccsoff1", NULL);
zfcp_erp_wait(adapter);
- zfcp_erp_thread_kill(adapter);
- up(&zfcp_data.config_sema);
+ mutex_unlock(&zfcp_data.config_mutex);
+out:
return 0;
}
@@ -240,11 +251,12 @@ static void zfcp_ccw_shutdown(struct ccw_device *cdev)
{
struct zfcp_adapter *adapter;
- down(&zfcp_data.config_sema);
+ mutex_lock(&zfcp_data.config_mutex);
adapter = dev_get_drvdata(&cdev->dev);
zfcp_erp_adapter_shutdown(adapter, 0, "ccshut1", NULL);
zfcp_erp_wait(adapter);
- up(&zfcp_data.config_sema);
+ zfcp_erp_thread_kill(adapter);
+ mutex_unlock(&zfcp_data.config_mutex);
}
static struct ccw_driver zfcp_ccw_driver = {
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index b99b87ce5a3..215b70749e9 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -3,7 +3,7 @@
*
* Debug traces for zfcp.
*
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
*/
#define KMSG_COMPONENT "zfcp"
@@ -11,6 +11,7 @@
#include <linux/ctype.h>
#include <asm/debug.h>
+#include "zfcp_dbf.h"
#include "zfcp_ext.h"
static u32 dbfsize = 4;
@@ -37,19 +38,6 @@ static void zfcp_dbf_hexdump(debug_info_t *dbf, void *to, int to_len,
}
}
-/* FIXME: this duplicate this code in s390 debug feature */
-static void zfcp_dbf_timestamp(unsigned long long stck, struct timespec *time)
-{
- unsigned long long sec;
-
- stck -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096);
- sec = stck >> 12;
- do_div(sec, 1000000);
- time->tv_sec = sec;
- stck -= (sec * 1000000) << 12;
- time->tv_nsec = ((stck * 1000) >> 12);
-}
-
static void zfcp_dbf_tag(char **p, const char *label, const char *tag)
{
int i;
@@ -106,7 +94,7 @@ static int zfcp_dbf_view_header(debug_info_t *id, struct debug_view *view,
char *p = out_buf;
if (strncmp(dump->tag, "dump", ZFCP_DBF_TAG_SIZE) != 0) {
- zfcp_dbf_timestamp(entry->id.stck, &t);
+ stck_to_timespec(entry->id.stck, &t);
zfcp_dbf_out(&p, "timestamp", "%011lu:%06lu",
t.tv_sec, t.tv_nsec);
zfcp_dbf_out(&p, "cpu", "%02i", entry->id.fields.cpuid);
@@ -119,13 +107,10 @@ static int zfcp_dbf_view_header(debug_info_t *id, struct debug_view *view,
return p - out_buf;
}
-/**
- * zfcp_hba_dbf_event_fsf_response - trace event for request completion
- * @fsf_req: request that has been completed
- */
-void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req)
+void _zfcp_dbf_hba_fsf_response(const char *tag2, int level,
+ struct zfcp_fsf_req *fsf_req,
+ struct zfcp_dbf *dbf)
{
- struct zfcp_adapter *adapter = fsf_req->adapter;
struct fsf_qtcb *qtcb = fsf_req->qtcb;
union fsf_prot_status_qual *prot_status_qual =
&qtcb->prefix.prot_status_qual;
@@ -134,33 +119,14 @@ void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req)
struct zfcp_port *port;
struct zfcp_unit *unit;
struct zfcp_send_els *send_els;
- struct zfcp_hba_dbf_record *rec = &adapter->hba_dbf_buf;
- struct zfcp_hba_dbf_record_response *response = &rec->u.response;
- int level;
+ struct zfcp_dbf_hba_record *rec = &dbf->hba_buf;
+ struct zfcp_dbf_hba_record_response *response = &rec->u.response;
unsigned long flags;
- spin_lock_irqsave(&adapter->hba_dbf_lock, flags);
+ spin_lock_irqsave(&dbf->hba_lock, flags);
memset(rec, 0, sizeof(*rec));
strncpy(rec->tag, "resp", ZFCP_DBF_TAG_SIZE);
-
- if ((qtcb->prefix.prot_status != FSF_PROT_GOOD) &&
- (qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) {
- strncpy(rec->tag2, "perr", ZFCP_DBF_TAG_SIZE);
- level = 1;
- } else if (qtcb->header.fsf_status != FSF_GOOD) {
- strncpy(rec->tag2, "ferr", ZFCP_DBF_TAG_SIZE);
- level = 1;
- } else if ((fsf_req->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) ||
- (fsf_req->fsf_command == FSF_QTCB_OPEN_LUN)) {
- strncpy(rec->tag2, "open", ZFCP_DBF_TAG_SIZE);
- level = 4;
- } else if (qtcb->header.log_length) {
- strncpy(rec->tag2, "qtcb", ZFCP_DBF_TAG_SIZE);
- level = 5;
- } else {
- strncpy(rec->tag2, "norm", ZFCP_DBF_TAG_SIZE);
- level = 6;
- }
+ strncpy(rec->tag2, tag2, ZFCP_DBF_TAG_SIZE);
response->fsf_command = fsf_req->fsf_command;
response->fsf_reqid = fsf_req->req_id;
@@ -173,9 +139,9 @@ void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req)
memcpy(response->fsf_status_qual,
fsf_status_qual, FSF_STATUS_QUALIFIER_SIZE);
response->fsf_req_status = fsf_req->status;
- response->sbal_first = fsf_req->sbal_first;
- response->sbal_last = fsf_req->sbal_last;
- response->sbal_response = fsf_req->sbal_response;
+ response->sbal_first = fsf_req->queue_req.sbal_first;
+ response->sbal_last = fsf_req->queue_req.sbal_last;
+ response->sbal_response = fsf_req->queue_req.sbal_response;
response->pool = fsf_req->pool != NULL;
response->erp_action = (unsigned long)fsf_req->erp_action;
@@ -224,7 +190,7 @@ void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req)
break;
}
- debug_event(adapter->hba_dbf, level, rec, sizeof(*rec));
+ debug_event(dbf->hba, level, rec, sizeof(*rec));
/* have fcp channel microcode fixed to use as little as possible */
if (fsf_req->fsf_command != FSF_QTCB_FCP_CMND) {
@@ -232,31 +198,25 @@ void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req)
char *buf = (char *)qtcb + qtcb->header.log_start;
int len = qtcb->header.log_length;
for (; len && !buf[len - 1]; len--);
- zfcp_dbf_hexdump(adapter->hba_dbf, rec, sizeof(*rec), level,
- buf, len);
+ zfcp_dbf_hexdump(dbf->hba, rec, sizeof(*rec), level, buf,
+ len);
}
- spin_unlock_irqrestore(&adapter->hba_dbf_lock, flags);
+ spin_unlock_irqrestore(&dbf->hba_lock, flags);
}
-/**
- * zfcp_hba_dbf_event_fsf_unsol - trace event for an unsolicited status buffer
- * @tag: tag indicating which kind of unsolicited status has been received
- * @adapter: adapter that has issued the unsolicited status buffer
- * @status_buffer: buffer containing payload of unsolicited status
- */
-void zfcp_hba_dbf_event_fsf_unsol(const char *tag, struct zfcp_adapter *adapter,
- struct fsf_status_read_buffer *status_buffer)
+void _zfcp_dbf_hba_fsf_unsol(const char *tag, int level, struct zfcp_dbf *dbf,
+ struct fsf_status_read_buffer *status_buffer)
{
- struct zfcp_hba_dbf_record *rec = &adapter->hba_dbf_buf;
+ struct zfcp_dbf_hba_record *rec = &dbf->hba_buf;
unsigned long flags;
- spin_lock_irqsave(&adapter->hba_dbf_lock, flags);
+ spin_lock_irqsave(&dbf->hba_lock, flags);
memset(rec, 0, sizeof(*rec));
strncpy(rec->tag, "stat", ZFCP_DBF_TAG_SIZE);
strncpy(rec->tag2, tag, ZFCP_DBF_TAG_SIZE);
- rec->u.status.failed = atomic_read(&adapter->stat_miss);
+ rec->u.status.failed = atomic_read(&dbf->adapter->stat_miss);
if (status_buffer != NULL) {
rec->u.status.status_type = status_buffer->status_type;
rec->u.status.status_subtype = status_buffer->status_subtype;
@@ -293,63 +253,61 @@ void zfcp_hba_dbf_event_fsf_unsol(const char *tag, struct zfcp_adapter *adapter,
&status_buffer->payload, rec->u.status.payload_size);
}
- debug_event(adapter->hba_dbf, 2, rec, sizeof(*rec));
- spin_unlock_irqrestore(&adapter->hba_dbf_lock, flags);
+ debug_event(dbf->hba, level, rec, sizeof(*rec));
+ spin_unlock_irqrestore(&dbf->hba_lock, flags);
}
/**
- * zfcp_hba_dbf_event_qdio - trace event for QDIO related failure
- * @adapter: adapter affected by this QDIO related event
+ * zfcp_dbf_hba_qdio - trace event for QDIO related failure
+ * @qdio: qdio structure affected by this QDIO related event
* @qdio_error: as passed by qdio module
* @sbal_index: first buffer with error condition, as passed by qdio module
* @sbal_count: number of buffers affected, as passed by qdio module
*/
-void zfcp_hba_dbf_event_qdio(struct zfcp_adapter *adapter,
- unsigned int qdio_error, int sbal_index,
- int sbal_count)
+void zfcp_dbf_hba_qdio(struct zfcp_dbf *dbf, unsigned int qdio_error,
+ int sbal_index, int sbal_count)
{
- struct zfcp_hba_dbf_record *r = &adapter->hba_dbf_buf;
+ struct zfcp_dbf_hba_record *r = &dbf->hba_buf;
unsigned long flags;
- spin_lock_irqsave(&adapter->hba_dbf_lock, flags);
+ spin_lock_irqsave(&dbf->hba_lock, flags);
memset(r, 0, sizeof(*r));
strncpy(r->tag, "qdio", ZFCP_DBF_TAG_SIZE);
r->u.qdio.qdio_error = qdio_error;
r->u.qdio.sbal_index = sbal_index;
r->u.qdio.sbal_count = sbal_count;
- debug_event(adapter->hba_dbf, 0, r, sizeof(*r));
- spin_unlock_irqrestore(&adapter->hba_dbf_lock, flags);
+ debug_event(dbf->hba, 0, r, sizeof(*r));
+ spin_unlock_irqrestore(&dbf->hba_lock, flags);
}
/**
- * zfcp_hba_dbf_event_berr - trace event for bit error threshold
- * @adapter: adapter affected by this QDIO related event
+ * zfcp_dbf_hba_berr - trace event for bit error threshold
+ * @dbf: dbf structure affected by this QDIO related event
* @req: fsf request
*/
-void zfcp_hba_dbf_event_berr(struct zfcp_adapter *adapter,
- struct zfcp_fsf_req *req)
+void zfcp_dbf_hba_berr(struct zfcp_dbf *dbf, struct zfcp_fsf_req *req)
{
- struct zfcp_hba_dbf_record *r = &adapter->hba_dbf_buf;
+ struct zfcp_dbf_hba_record *r = &dbf->hba_buf;
struct fsf_status_read_buffer *sr_buf = req->data;
struct fsf_bit_error_payload *err = &sr_buf->payload.bit_error;
unsigned long flags;
- spin_lock_irqsave(&adapter->hba_dbf_lock, flags);
+ spin_lock_irqsave(&dbf->hba_lock, flags);
memset(r, 0, sizeof(*r));
strncpy(r->tag, "berr", ZFCP_DBF_TAG_SIZE);
memcpy(&r->u.berr, err, sizeof(struct fsf_bit_error_payload));
- debug_event(adapter->hba_dbf, 0, r, sizeof(*r));
- spin_unlock_irqrestore(&adapter->hba_dbf_lock, flags);
+ debug_event(dbf->hba, 0, r, sizeof(*r));
+ spin_unlock_irqrestore(&dbf->hba_lock, flags);
}
-static void zfcp_hba_dbf_view_response(char **p,
- struct zfcp_hba_dbf_record_response *r)
+static void zfcp_dbf_hba_view_response(char **p,
+ struct zfcp_dbf_hba_record_response *r)
{
struct timespec t;
zfcp_dbf_out(p, "fsf_command", "0x%08x", r->fsf_command);
zfcp_dbf_out(p, "fsf_reqid", "0x%0Lx", r->fsf_reqid);
zfcp_dbf_out(p, "fsf_seqno", "0x%08x", r->fsf_seqno);
- zfcp_dbf_timestamp(r->fsf_issued, &t);
+ stck_to_timespec(r->fsf_issued, &t);
zfcp_dbf_out(p, "fsf_issued", "%011lu:%06lu", t.tv_sec, t.tv_nsec);
zfcp_dbf_out(p, "fsf_prot_status", "0x%08x", r->fsf_prot_status);
zfcp_dbf_out(p, "fsf_status", "0x%08x", r->fsf_status);
@@ -403,8 +361,8 @@ static void zfcp_hba_dbf_view_response(char **p,
}
}
-static void zfcp_hba_dbf_view_status(char **p,
- struct zfcp_hba_dbf_record_status *r)
+static void zfcp_dbf_hba_view_status(char **p,
+ struct zfcp_dbf_hba_record_status *r)
{
zfcp_dbf_out(p, "failed", "0x%02x", r->failed);
zfcp_dbf_out(p, "status_type", "0x%08x", r->status_type);
@@ -416,14 +374,14 @@ static void zfcp_hba_dbf_view_status(char **p,
r->payload_size);
}
-static void zfcp_hba_dbf_view_qdio(char **p, struct zfcp_hba_dbf_record_qdio *r)
+static void zfcp_dbf_hba_view_qdio(char **p, struct zfcp_dbf_hba_record_qdio *r)
{
zfcp_dbf_out(p, "qdio_error", "0x%08x", r->qdio_error);
zfcp_dbf_out(p, "sbal_index", "0x%02x", r->sbal_index);
zfcp_dbf_out(p, "sbal_count", "0x%02x", r->sbal_count);
}
-static void zfcp_hba_dbf_view_berr(char **p, struct fsf_bit_error_payload *r)
+static void zfcp_dbf_hba_view_berr(char **p, struct fsf_bit_error_payload *r)
{
zfcp_dbf_out(p, "link_failures", "%d", r->link_failure_error_count);
zfcp_dbf_out(p, "loss_of_sync_err", "%d", r->loss_of_sync_error_count);
@@ -447,10 +405,10 @@ static void zfcp_hba_dbf_view_berr(char **p, struct fsf_bit_error_payload *r)
r->current_transmit_b2b_credit);
}
-static int zfcp_hba_dbf_view_format(debug_info_t *id, struct debug_view *view,
+static int zfcp_dbf_hba_view_format(debug_info_t *id, struct debug_view *view,
char *out_buf, const char *in_buf)
{
- struct zfcp_hba_dbf_record *r = (struct zfcp_hba_dbf_record *)in_buf;
+ struct zfcp_dbf_hba_record *r = (struct zfcp_dbf_hba_record *)in_buf;
char *p = out_buf;
if (strncmp(r->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0)
@@ -461,45 +419,42 @@ static int zfcp_hba_dbf_view_format(debug_info_t *id, struct debug_view *view,
zfcp_dbf_tag(&p, "tag2", r->tag2);
if (strncmp(r->tag, "resp", ZFCP_DBF_TAG_SIZE) == 0)
- zfcp_hba_dbf_view_response(&p, &r->u.response);
+ zfcp_dbf_hba_view_response(&p, &r->u.response);
else if (strncmp(r->tag, "stat", ZFCP_DBF_TAG_SIZE) == 0)
- zfcp_hba_dbf_view_status(&p, &r->u.status);
+ zfcp_dbf_hba_view_status(&p, &r->u.status);
else if (strncmp(r->tag, "qdio", ZFCP_DBF_TAG_SIZE) == 0)
- zfcp_hba_dbf_view_qdio(&p, &r->u.qdio);
+ zfcp_dbf_hba_view_qdio(&p, &r->u.qdio);
else if (strncmp(r->tag, "berr", ZFCP_DBF_TAG_SIZE) == 0)
- zfcp_hba_dbf_view_berr(&p, &r->u.berr);
+ zfcp_dbf_hba_view_berr(&p, &r->u.berr);
if (strncmp(r->tag, "resp", ZFCP_DBF_TAG_SIZE) != 0)
p += sprintf(p, "\n");
return p - out_buf;
}
-static struct debug_view zfcp_hba_dbf_view = {
- "structured",
- NULL,
- &zfcp_dbf_view_header,
- &zfcp_hba_dbf_view_format,
- NULL,
- NULL
+static struct debug_view zfcp_dbf_hba_view = {
+ .name = "structured",
+ .header_proc = zfcp_dbf_view_header,
+ .format_proc = zfcp_dbf_hba_view_format,
};
-static const char *zfcp_rec_dbf_tags[] = {
+static const char *zfcp_dbf_rec_tags[] = {
[ZFCP_REC_DBF_ID_THREAD] = "thread",
[ZFCP_REC_DBF_ID_TARGET] = "target",
[ZFCP_REC_DBF_ID_TRIGGER] = "trigger",
[ZFCP_REC_DBF_ID_ACTION] = "action",
};
-static int zfcp_rec_dbf_view_format(debug_info_t *id, struct debug_view *view,
+static int zfcp_dbf_rec_view_format(debug_info_t *id, struct debug_view *view,
char *buf, const char *_rec)
{
- struct zfcp_rec_dbf_record *r = (struct zfcp_rec_dbf_record *)_rec;
+ struct zfcp_dbf_rec_record *r = (struct zfcp_dbf_rec_record *)_rec;
char *p = buf;
char hint[ZFCP_DBF_ID_SIZE + 1];
memcpy(hint, r->id2, ZFCP_DBF_ID_SIZE);
hint[ZFCP_DBF_ID_SIZE] = 0;
- zfcp_dbf_outs(&p, "tag", zfcp_rec_dbf_tags[r->id]);
+ zfcp_dbf_outs(&p, "tag", zfcp_dbf_rec_tags[r->id]);
zfcp_dbf_outs(&p, "hint", hint);
switch (r->id) {
case ZFCP_REC_DBF_ID_THREAD:
@@ -537,24 +492,22 @@ static int zfcp_rec_dbf_view_format(debug_info_t *id, struct debug_view *view,
return p - buf;
}
-static struct debug_view zfcp_rec_dbf_view = {
- "structured",
- NULL,
- &zfcp_dbf_view_header,
- &zfcp_rec_dbf_view_format,
- NULL,
- NULL
+static struct debug_view zfcp_dbf_rec_view = {
+ .name = "structured",
+ .header_proc = zfcp_dbf_view_header,
+ .format_proc = zfcp_dbf_rec_view_format,
};
/**
- * zfcp_rec_dbf_event_thread - trace event related to recovery thread operation
+ * zfcp_dbf_rec_thread - trace event related to recovery thread operation
* @id2: identifier for event
- * @adapter: adapter
+ * @dbf: reference to dbf structure
* This function assumes that the caller is holding erp_lock.
*/
-void zfcp_rec_dbf_event_thread(char *id2, struct zfcp_adapter *adapter)
+void zfcp_dbf_rec_thread(char *id2, struct zfcp_dbf *dbf)
{
- struct zfcp_rec_dbf_record *r = &adapter->rec_dbf_buf;
+ struct zfcp_adapter *adapter = dbf->adapter;
+ struct zfcp_dbf_rec_record *r = &dbf->rec_buf;
unsigned long flags = 0;
struct list_head *entry;
unsigned ready = 0, running = 0, total;
@@ -565,41 +518,41 @@ void zfcp_rec_dbf_event_thread(char *id2, struct zfcp_adapter *adapter)
running++;
total = adapter->erp_total_count;
- spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
+ spin_lock_irqsave(&dbf->rec_lock, flags);
memset(r, 0, sizeof(*r));
r->id = ZFCP_REC_DBF_ID_THREAD;
memcpy(r->id2, id2, ZFCP_DBF_ID_SIZE);
r->u.thread.total = total;
r->u.thread.ready = ready;
r->u.thread.running = running;
- debug_event(adapter->rec_dbf, 6, r, sizeof(*r));
- spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
+ debug_event(dbf->rec, 6, r, sizeof(*r));
+ spin_unlock_irqrestore(&dbf->rec_lock, flags);
}
/**
- * zfcp_rec_dbf_event_thread - trace event related to recovery thread operation
+ * zfcp_dbf_rec_thread - trace event related to recovery thread operation
* @id2: identifier for event
* @adapter: adapter
* This function assumes that the caller does not hold erp_lock.
*/
-void zfcp_rec_dbf_event_thread_lock(char *id2, struct zfcp_adapter *adapter)
+void zfcp_dbf_rec_thread_lock(char *id2, struct zfcp_dbf *dbf)
{
+ struct zfcp_adapter *adapter = dbf->adapter;
unsigned long flags;
read_lock_irqsave(&adapter->erp_lock, flags);
- zfcp_rec_dbf_event_thread(id2, adapter);
+ zfcp_dbf_rec_thread(id2, dbf);
read_unlock_irqrestore(&adapter->erp_lock, flags);
}
-static void zfcp_rec_dbf_event_target(char *id2, void *ref,
- struct zfcp_adapter *adapter,
- atomic_t *status, atomic_t *erp_count,
- u64 wwpn, u32 d_id, u64 fcp_lun)
+static void zfcp_dbf_rec_target(char *id2, void *ref, struct zfcp_dbf *dbf,
+ atomic_t *status, atomic_t *erp_count, u64 wwpn,
+ u32 d_id, u64 fcp_lun)
{
- struct zfcp_rec_dbf_record *r = &adapter->rec_dbf_buf;
+ struct zfcp_dbf_rec_record *r = &dbf->rec_buf;
unsigned long flags;
- spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
+ spin_lock_irqsave(&dbf->rec_lock, flags);
memset(r, 0, sizeof(*r));
r->id = ZFCP_REC_DBF_ID_TARGET;
memcpy(r->id2, id2, ZFCP_DBF_ID_SIZE);
@@ -609,56 +562,57 @@ static void zfcp_rec_dbf_event_target(char *id2, void *ref,
r->u.target.d_id = d_id;
r->u.target.fcp_lun = fcp_lun;
r->u.target.erp_count = atomic_read(erp_count);
- debug_event(adapter->rec_dbf, 3, r, sizeof(*r));
- spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
+ debug_event(dbf->rec, 3, r, sizeof(*r));
+ spin_unlock_irqrestore(&dbf->rec_lock, flags);
}
/**
- * zfcp_rec_dbf_event_adapter - trace event for adapter state change
+ * zfcp_dbf_rec_adapter - trace event for adapter state change
* @id: identifier for trigger of state change
* @ref: additional reference (e.g. request)
- * @adapter: adapter
+ * @dbf: reference to dbf structure
*/
-void zfcp_rec_dbf_event_adapter(char *id, void *ref,
- struct zfcp_adapter *adapter)
+void zfcp_dbf_rec_adapter(char *id, void *ref, struct zfcp_dbf *dbf)
{
- zfcp_rec_dbf_event_target(id, ref, adapter, &adapter->status,
+ struct zfcp_adapter *adapter = dbf->adapter;
+
+ zfcp_dbf_rec_target(id, ref, dbf, &adapter->status,
&adapter->erp_counter, 0, 0, 0);
}
/**
- * zfcp_rec_dbf_event_port - trace event for port state change
+ * zfcp_dbf_rec_port - trace event for port state change
* @id: identifier for trigger of state change
* @ref: additional reference (e.g. request)
* @port: port
*/
-void zfcp_rec_dbf_event_port(char *id, void *ref, struct zfcp_port *port)
+void zfcp_dbf_rec_port(char *id, void *ref, struct zfcp_port *port)
{
- struct zfcp_adapter *adapter = port->adapter;
+ struct zfcp_dbf *dbf = port->adapter->dbf;
- zfcp_rec_dbf_event_target(id, ref, adapter, &port->status,
+ zfcp_dbf_rec_target(id, ref, dbf, &port->status,
&port->erp_counter, port->wwpn, port->d_id,
0);
}
/**
- * zfcp_rec_dbf_event_unit - trace event for unit state change
+ * zfcp_dbf_rec_unit - trace event for unit state change
* @id: identifier for trigger of state change
* @ref: additional reference (e.g. request)
* @unit: unit
*/
-void zfcp_rec_dbf_event_unit(char *id, void *ref, struct zfcp_unit *unit)
+void zfcp_dbf_rec_unit(char *id, void *ref, struct zfcp_unit *unit)
{
struct zfcp_port *port = unit->port;
- struct zfcp_adapter *adapter = port->adapter;
+ struct zfcp_dbf *dbf = port->adapter->dbf;
- zfcp_rec_dbf_event_target(id, ref, adapter, &unit->status,
+ zfcp_dbf_rec_target(id, ref, dbf, &unit->status,
&unit->erp_counter, port->wwpn, port->d_id,
unit->fcp_lun);
}
/**
- * zfcp_rec_dbf_event_trigger - trace event for triggered error recovery
+ * zfcp_dbf_rec_trigger - trace event for triggered error recovery
* @id2: identifier for error recovery trigger
* @ref: additional reference (e.g. request)
* @want: originally requested error recovery action
@@ -668,14 +622,15 @@ void zfcp_rec_dbf_event_unit(char *id, void *ref, struct zfcp_unit *unit)
* @port: port
* @unit: unit
*/
-void zfcp_rec_dbf_event_trigger(char *id2, void *ref, u8 want, u8 need,
- void *action, struct zfcp_adapter *adapter,
- struct zfcp_port *port, struct zfcp_unit *unit)
+void zfcp_dbf_rec_trigger(char *id2, void *ref, u8 want, u8 need, void *action,
+ struct zfcp_adapter *adapter, struct zfcp_port *port,
+ struct zfcp_unit *unit)
{
- struct zfcp_rec_dbf_record *r = &adapter->rec_dbf_buf;
+ struct zfcp_dbf *dbf = adapter->dbf;
+ struct zfcp_dbf_rec_record *r = &dbf->rec_buf;
unsigned long flags;
- spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
+ spin_lock_irqsave(&dbf->rec_lock, flags);
memset(r, 0, sizeof(*r));
r->id = ZFCP_REC_DBF_ID_TRIGGER;
memcpy(r->id2, id2, ZFCP_DBF_ID_SIZE);
@@ -692,22 +647,22 @@ void zfcp_rec_dbf_event_trigger(char *id2, void *ref, u8 want, u8 need,
r->u.trigger.us = atomic_read(&unit->status);
r->u.trigger.fcp_lun = unit->fcp_lun;
}
- debug_event(adapter->rec_dbf, action ? 1 : 4, r, sizeof(*r));
- spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
+ debug_event(dbf->rec, action ? 1 : 4, r, sizeof(*r));
+ spin_unlock_irqrestore(&dbf->rec_lock, flags);
}
/**
- * zfcp_rec_dbf_event_action - trace event showing progress of recovery action
+ * zfcp_dbf_rec_action - trace event showing progress of recovery action
* @id2: identifier
* @erp_action: error recovery action struct pointer
*/
-void zfcp_rec_dbf_event_action(char *id2, struct zfcp_erp_action *erp_action)
+void zfcp_dbf_rec_action(char *id2, struct zfcp_erp_action *erp_action)
{
- struct zfcp_adapter *adapter = erp_action->adapter;
- struct zfcp_rec_dbf_record *r = &adapter->rec_dbf_buf;
+ struct zfcp_dbf *dbf = erp_action->adapter->dbf;
+ struct zfcp_dbf_rec_record *r = &dbf->rec_buf;
unsigned long flags;
- spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
+ spin_lock_irqsave(&dbf->rec_lock, flags);
memset(r, 0, sizeof(*r));
r->id = ZFCP_REC_DBF_ID_ACTION;
memcpy(r->id2, id2, ZFCP_DBF_ID_SIZE);
@@ -715,26 +670,27 @@ void zfcp_rec_dbf_event_action(char *id2, struct zfcp_erp_action *erp_action)
r->u.action.status = erp_action->status;
r->u.action.step = erp_action->step;
r->u.action.fsf_req = (unsigned long)erp_action->fsf_req;
- debug_event(adapter->rec_dbf, 5, r, sizeof(*r));
- spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
+ debug_event(dbf->rec, 5, r, sizeof(*r));
+ spin_unlock_irqrestore(&dbf->rec_lock, flags);
}
/**
- * zfcp_san_dbf_event_ct_request - trace event for issued CT request
+ * zfcp_dbf_san_ct_request - trace event for issued CT request
* @fsf_req: request containing issued CT data
*/
-void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req)
+void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *fsf_req)
{
struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data;
struct zfcp_wka_port *wka_port = ct->wka_port;
struct zfcp_adapter *adapter = wka_port->adapter;
+ struct zfcp_dbf *dbf = adapter->dbf;
struct ct_hdr *hdr = sg_virt(ct->req);
- struct zfcp_san_dbf_record *r = &adapter->san_dbf_buf;
- struct zfcp_san_dbf_record_ct_request *oct = &r->u.ct_req;
+ struct zfcp_dbf_san_record *r = &dbf->san_buf;
+ struct zfcp_dbf_san_record_ct_request *oct = &r->u.ct_req;
int level = 3;
unsigned long flags;
- spin_lock_irqsave(&adapter->san_dbf_lock, flags);
+ spin_lock_irqsave(&dbf->san_lock, flags);
memset(r, 0, sizeof(*r));
strncpy(r->tag, "octc", ZFCP_DBF_TAG_SIZE);
r->fsf_reqid = fsf_req->req_id;
@@ -749,28 +705,29 @@ void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req)
oct->max_res_size = hdr->max_res_size;
oct->len = min((int)ct->req->length - (int)sizeof(struct ct_hdr),
ZFCP_DBF_SAN_MAX_PAYLOAD);
- debug_event(adapter->san_dbf, level, r, sizeof(*r));
- zfcp_dbf_hexdump(adapter->san_dbf, r, sizeof(*r), level,
+ debug_event(dbf->san, level, r, sizeof(*r));
+ zfcp_dbf_hexdump(dbf->san, r, sizeof(*r), level,
(void *)hdr + sizeof(struct ct_hdr), oct->len);
- spin_unlock_irqrestore(&adapter->san_dbf_lock, flags);
+ spin_unlock_irqrestore(&dbf->san_lock, flags);
}
/**
- * zfcp_san_dbf_event_ct_response - trace event for completion of CT request
+ * zfcp_dbf_san_ct_response - trace event for completion of CT request
* @fsf_req: request containing CT response
*/
-void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req)
+void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *fsf_req)
{
struct zfcp_send_ct *ct = (struct zfcp_send_ct *)fsf_req->data;
struct zfcp_wka_port *wka_port = ct->wka_port;
struct zfcp_adapter *adapter = wka_port->adapter;
struct ct_hdr *hdr = sg_virt(ct->resp);
- struct zfcp_san_dbf_record *r = &adapter->san_dbf_buf;
- struct zfcp_san_dbf_record_ct_response *rct = &r->u.ct_resp;
+ struct zfcp_dbf *dbf = adapter->dbf;
+ struct zfcp_dbf_san_record *r = &dbf->san_buf;
+ struct zfcp_dbf_san_record_ct_response *rct = &r->u.ct_resp;
int level = 3;
unsigned long flags;
- spin_lock_irqsave(&adapter->san_dbf_lock, flags);
+ spin_lock_irqsave(&dbf->san_lock, flags);
memset(r, 0, sizeof(*r));
strncpy(r->tag, "rctc", ZFCP_DBF_TAG_SIZE);
r->fsf_reqid = fsf_req->req_id;
@@ -785,22 +742,22 @@ void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req)
rct->max_res_size = hdr->max_res_size;
rct->len = min((int)ct->resp->length - (int)sizeof(struct ct_hdr),
ZFCP_DBF_SAN_MAX_PAYLOAD);
- debug_event(adapter->san_dbf, level, r, sizeof(*r));
- zfcp_dbf_hexdump(adapter->san_dbf, r, sizeof(*r), level,
+ debug_event(dbf->san, level, r, sizeof(*r));
+ zfcp_dbf_hexdump(dbf->san, r, sizeof(*r), level,
(void *)hdr + sizeof(struct ct_hdr), rct->len);
- spin_unlock_irqrestore(&adapter->san_dbf_lock, flags);
+ spin_unlock_irqrestore(&dbf->san_lock, flags);
}
-static void zfcp_san_dbf_event_els(const char *tag, int level,
- struct zfcp_fsf_req *fsf_req, u32 s_id,
- u32 d_id, u8 ls_code, void *buffer,
- int buflen)
+static void zfcp_dbf_san_els(const char *tag, int level,
+ struct zfcp_fsf_req *fsf_req, u32 s_id, u32 d_id,
+ u8 ls_code, void *buffer, int buflen)
{
struct zfcp_adapter *adapter = fsf_req->adapter;
- struct zfcp_san_dbf_record *rec = &adapter->san_dbf_buf;
+ struct zfcp_dbf *dbf = adapter->dbf;
+ struct zfcp_dbf_san_record *rec = &dbf->san_buf;
unsigned long flags;
- spin_lock_irqsave(&adapter->san_dbf_lock, flags);
+ spin_lock_irqsave(&dbf->san_lock, flags);
memset(rec, 0, sizeof(*rec));
strncpy(rec->tag, tag, ZFCP_DBF_TAG_SIZE);
rec->fsf_reqid = fsf_req->req_id;
@@ -808,45 +765,45 @@ static void zfcp_san_dbf_event_els(const char *tag, int level,
rec->s_id = s_id;
rec->d_id = d_id;
rec->u.els.ls_code = ls_code;
- debug_event(adapter->san_dbf, level, rec, sizeof(*rec));
- zfcp_dbf_hexdump(adapter->san_dbf, rec, sizeof(*rec), level,
+ debug_event(dbf->san, level, rec, sizeof(*rec));
+ zfcp_dbf_hexdump(dbf->san, rec, sizeof(*rec), level,
buffer, min(buflen, ZFCP_DBF_SAN_MAX_PAYLOAD));
- spin_unlock_irqrestore(&adapter->san_dbf_lock, flags);
+ spin_unlock_irqrestore(&dbf->san_lock, flags);
}
/**
- * zfcp_san_dbf_event_els_request - trace event for issued ELS
+ * zfcp_dbf_san_els_request - trace event for issued ELS
* @fsf_req: request containing issued ELS
*/
-void zfcp_san_dbf_event_els_request(struct zfcp_fsf_req *fsf_req)
+void zfcp_dbf_san_els_request(struct zfcp_fsf_req *fsf_req)
{
struct zfcp_send_els *els = (struct zfcp_send_els *)fsf_req->data;
- zfcp_san_dbf_event_els("oels", 2, fsf_req,
+ zfcp_dbf_san_els("oels", 2, fsf_req,
fc_host_port_id(els->adapter->scsi_host),
els->d_id, *(u8 *) sg_virt(els->req),
sg_virt(els->req), els->req->length);
}
/**
- * zfcp_san_dbf_event_els_response - trace event for completed ELS
+ * zfcp_dbf_san_els_response - trace event for completed ELS
* @fsf_req: request containing ELS response
*/
-void zfcp_san_dbf_event_els_response(struct zfcp_fsf_req *fsf_req)
+void zfcp_dbf_san_els_response(struct zfcp_fsf_req *fsf_req)
{
struct zfcp_send_els *els = (struct zfcp_send_els *)fsf_req->data;
- zfcp_san_dbf_event_els("rels", 2, fsf_req, els->d_id,
+ zfcp_dbf_san_els("rels", 2, fsf_req, els->d_id,
fc_host_port_id(els->adapter->scsi_host),
*(u8 *)sg_virt(els->req), sg_virt(els->resp),
els->resp->length);
}
/**
- * zfcp_san_dbf_event_incoming_els - trace event for incomig ELS
+ * zfcp_dbf_san_incoming_els - trace event for incomig ELS
* @fsf_req: request containing unsolicited status buffer with incoming ELS
*/
-void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *fsf_req)
+void zfcp_dbf_san_incoming_els(struct zfcp_fsf_req *fsf_req)
{
struct zfcp_adapter *adapter = fsf_req->adapter;
struct fsf_status_read_buffer *buf =
@@ -854,16 +811,16 @@ void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *fsf_req)
int length = (int)buf->length -
(int)((void *)&buf->payload - (void *)buf);
- zfcp_san_dbf_event_els("iels", 1, fsf_req, buf->d_id,
+ zfcp_dbf_san_els("iels", 1, fsf_req, buf->d_id,
fc_host_port_id(adapter->scsi_host),
buf->payload.data[0], (void *)buf->payload.data,
length);
}
-static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view,
+static int zfcp_dbf_san_view_format(debug_info_t *id, struct debug_view *view,
char *out_buf, const char *in_buf)
{
- struct zfcp_san_dbf_record *r = (struct zfcp_san_dbf_record *)in_buf;
+ struct zfcp_dbf_san_record *r = (struct zfcp_dbf_san_record *)in_buf;
char *p = out_buf;
if (strncmp(r->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0)
@@ -876,7 +833,7 @@ static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view,
zfcp_dbf_out(&p, "d_id", "0x%06x", r->d_id);
if (strncmp(r->tag, "octc", ZFCP_DBF_TAG_SIZE) == 0) {
- struct zfcp_san_dbf_record_ct_request *ct = &r->u.ct_req;
+ struct zfcp_dbf_san_record_ct_request *ct = &r->u.ct_req;
zfcp_dbf_out(&p, "cmd_req_code", "0x%04x", ct->cmd_req_code);
zfcp_dbf_out(&p, "revision", "0x%02x", ct->revision);
zfcp_dbf_out(&p, "gs_type", "0x%02x", ct->gs_type);
@@ -884,7 +841,7 @@ static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view,
zfcp_dbf_out(&p, "options", "0x%02x", ct->options);
zfcp_dbf_out(&p, "max_res_size", "0x%04x", ct->max_res_size);
} else if (strncmp(r->tag, "rctc", ZFCP_DBF_TAG_SIZE) == 0) {
- struct zfcp_san_dbf_record_ct_response *ct = &r->u.ct_resp;
+ struct zfcp_dbf_san_record_ct_response *ct = &r->u.ct_resp;
zfcp_dbf_out(&p, "cmd_rsp_code", "0x%04x", ct->cmd_rsp_code);
zfcp_dbf_out(&p, "revision", "0x%02x", ct->revision);
zfcp_dbf_out(&p, "reason_code", "0x%02x", ct->reason_code);
@@ -894,35 +851,30 @@ static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view,
} else if (strncmp(r->tag, "oels", ZFCP_DBF_TAG_SIZE) == 0 ||
strncmp(r->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 ||
strncmp(r->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) {
- struct zfcp_san_dbf_record_els *els = &r->u.els;
+ struct zfcp_dbf_san_record_els *els = &r->u.els;
zfcp_dbf_out(&p, "ls_code", "0x%02x", els->ls_code);
}
return p - out_buf;
}
-static struct debug_view zfcp_san_dbf_view = {
- "structured",
- NULL,
- &zfcp_dbf_view_header,
- &zfcp_san_dbf_view_format,
- NULL,
- NULL
+static struct debug_view zfcp_dbf_san_view = {
+ .name = "structured",
+ .header_proc = zfcp_dbf_view_header,
+ .format_proc = zfcp_dbf_san_view_format,
};
-static void zfcp_scsi_dbf_event(const char *tag, const char *tag2, int level,
- struct zfcp_adapter *adapter,
- struct scsi_cmnd *scsi_cmnd,
- struct zfcp_fsf_req *fsf_req,
- unsigned long old_req_id)
+void _zfcp_dbf_scsi(const char *tag, const char *tag2, int level,
+ struct zfcp_dbf *dbf, struct scsi_cmnd *scsi_cmnd,
+ struct zfcp_fsf_req *fsf_req, unsigned long old_req_id)
{
- struct zfcp_scsi_dbf_record *rec = &adapter->scsi_dbf_buf;
+ struct zfcp_dbf_scsi_record *rec = &dbf->scsi_buf;
struct zfcp_dbf_dump *dump = (struct zfcp_dbf_dump *)rec;
unsigned long flags;
struct fcp_rsp_iu *fcp_rsp;
char *fcp_rsp_info = NULL, *fcp_sns_info = NULL;
int offset = 0, buflen = 0;
- spin_lock_irqsave(&adapter->scsi_dbf_lock, flags);
+ spin_lock_irqsave(&dbf->scsi_lock, flags);
do {
memset(rec, 0, sizeof(*rec));
if (offset == 0) {
@@ -976,68 +928,20 @@ static void zfcp_scsi_dbf_event(const char *tag, const char *tag2, int level,
dump->offset = offset;
dump->size = min(buflen - offset,
(int)sizeof(struct
- zfcp_scsi_dbf_record) -
+ zfcp_dbf_scsi_record) -
(int)sizeof(struct zfcp_dbf_dump));
memcpy(dump->data, fcp_sns_info + offset, dump->size);
offset += dump->size;
}
- debug_event(adapter->scsi_dbf, level, rec, sizeof(*rec));
+ debug_event(dbf->scsi, level, rec, sizeof(*rec));
} while (offset < buflen);
- spin_unlock_irqrestore(&adapter->scsi_dbf_lock, flags);
-}
-
-/**
- * zfcp_scsi_dbf_event_result - trace event for SCSI command completion
- * @tag: tag indicating success or failure of SCSI command
- * @level: trace level applicable for this event
- * @adapter: adapter that has been used to issue the SCSI command
- * @scsi_cmnd: SCSI command pointer
- * @fsf_req: request used to issue SCSI command (might be NULL)
- */
-void zfcp_scsi_dbf_event_result(const char *tag, int level,
- struct zfcp_adapter *adapter,
- struct scsi_cmnd *scsi_cmnd,
- struct zfcp_fsf_req *fsf_req)
-{
- zfcp_scsi_dbf_event("rslt", tag, level, adapter, scsi_cmnd, fsf_req, 0);
+ spin_unlock_irqrestore(&dbf->scsi_lock, flags);
}
-/**
- * zfcp_scsi_dbf_event_abort - trace event for SCSI command abort
- * @tag: tag indicating success or failure of abort operation
- * @adapter: adapter thas has been used to issue SCSI command to be aborted
- * @scsi_cmnd: SCSI command to be aborted
- * @new_fsf_req: request containing abort (might be NULL)
- * @old_req_id: identifier of request containg SCSI command to be aborted
- */
-void zfcp_scsi_dbf_event_abort(const char *tag, struct zfcp_adapter *adapter,
- struct scsi_cmnd *scsi_cmnd,
- struct zfcp_fsf_req *new_fsf_req,
- unsigned long old_req_id)
-{
- zfcp_scsi_dbf_event("abrt", tag, 1, adapter, scsi_cmnd, new_fsf_req,
- old_req_id);
-}
-
-/**
- * zfcp_scsi_dbf_event_devreset - trace event for Logical Unit or Target Reset
- * @tag: tag indicating success or failure of reset operation
- * @flag: indicates type of reset (Target Reset, Logical Unit Reset)
- * @unit: unit that needs reset
- * @scsi_cmnd: SCSI command which caused this error recovery
- */
-void zfcp_scsi_dbf_event_devreset(const char *tag, u8 flag,
- struct zfcp_unit *unit,
- struct scsi_cmnd *scsi_cmnd)
-{
- zfcp_scsi_dbf_event(flag == FCP_TARGET_RESET ? "trst" : "lrst", tag, 1,
- unit->port->adapter, scsi_cmnd, NULL, 0);
-}
-
-static int zfcp_scsi_dbf_view_format(debug_info_t *id, struct debug_view *view,
+static int zfcp_dbf_scsi_view_format(debug_info_t *id, struct debug_view *view,
char *out_buf, const char *in_buf)
{
- struct zfcp_scsi_dbf_record *r = (struct zfcp_scsi_dbf_record *)in_buf;
+ struct zfcp_dbf_scsi_record *r = (struct zfcp_dbf_scsi_record *)in_buf;
struct timespec t;
char *p = out_buf;
@@ -1059,7 +963,7 @@ static int zfcp_scsi_dbf_view_format(debug_info_t *id, struct debug_view *view,
zfcp_dbf_out(&p, "old_fsf_reqid", "0x%0Lx", r->old_fsf_reqid);
zfcp_dbf_out(&p, "fsf_reqid", "0x%0Lx", r->fsf_reqid);
zfcp_dbf_out(&p, "fsf_seqno", "0x%08x", r->fsf_seqno);
- zfcp_dbf_timestamp(r->fsf_issued, &t);
+ stck_to_timespec(r->fsf_issued, &t);
zfcp_dbf_out(&p, "fsf_issued", "%011lu:%06lu", t.tv_sec, t.tv_nsec);
if (strncmp(r->tag, "rslt", ZFCP_DBF_TAG_SIZE) == 0) {
@@ -1078,84 +982,96 @@ static int zfcp_scsi_dbf_view_format(debug_info_t *id, struct debug_view *view,
return p - out_buf;
}
-static struct debug_view zfcp_scsi_dbf_view = {
- "structured",
- NULL,
- &zfcp_dbf_view_header,
- &zfcp_scsi_dbf_view_format,
- NULL,
- NULL
+static struct debug_view zfcp_dbf_scsi_view = {
+ .name = "structured",
+ .header_proc = zfcp_dbf_view_header,
+ .format_proc = zfcp_dbf_scsi_view_format,
};
+static debug_info_t *zfcp_dbf_reg(const char *name, int level,
+ struct debug_view *view, int size)
+{
+ struct debug_info *d;
+
+ d = debug_register(name, dbfsize, level, size);
+ if (!d)
+ return NULL;
+
+ debug_register_view(d, &debug_hex_ascii_view);
+ debug_register_view(d, view);
+ debug_set_level(d, level);
+
+ return d;
+}
+
/**
* zfcp_adapter_debug_register - registers debug feature for an adapter
* @adapter: pointer to adapter for which debug features should be registered
* return: -ENOMEM on error, 0 otherwise
*/
-int zfcp_adapter_debug_register(struct zfcp_adapter *adapter)
+int zfcp_dbf_adapter_register(struct zfcp_adapter *adapter)
{
char dbf_name[DEBUG_MAX_NAME_LEN];
+ struct zfcp_dbf *dbf;
+
+ dbf = kmalloc(sizeof(struct zfcp_dbf), GFP_KERNEL);
+ if (!dbf)
+ return -ENOMEM;
+
+ dbf->adapter = adapter;
+
+ spin_lock_init(&dbf->hba_lock);
+ spin_lock_init(&dbf->san_lock);
+ spin_lock_init(&dbf->scsi_lock);
+ spin_lock_init(&dbf->rec_lock);
/* debug feature area which records recovery activity */
sprintf(dbf_name, "zfcp_%s_rec", dev_name(&adapter->ccw_device->dev));
- adapter->rec_dbf = debug_register(dbf_name, dbfsize, 1,
- sizeof(struct zfcp_rec_dbf_record));
- if (!adapter->rec_dbf)
- goto failed;
- debug_register_view(adapter->rec_dbf, &debug_hex_ascii_view);
- debug_register_view(adapter->rec_dbf, &zfcp_rec_dbf_view);
- debug_set_level(adapter->rec_dbf, 3);
+ dbf->rec = zfcp_dbf_reg(dbf_name, 3, &zfcp_dbf_rec_view,
+ sizeof(struct zfcp_dbf_rec_record));
+ if (!dbf->rec)
+ goto err_out;
/* debug feature area which records HBA (FSF and QDIO) conditions */
sprintf(dbf_name, "zfcp_%s_hba", dev_name(&adapter->ccw_device->dev));
- adapter->hba_dbf = debug_register(dbf_name, dbfsize, 1,
- sizeof(struct zfcp_hba_dbf_record));
- if (!adapter->hba_dbf)
- goto failed;
- debug_register_view(adapter->hba_dbf, &debug_hex_ascii_view);
- debug_register_view(adapter->hba_dbf, &zfcp_hba_dbf_view);
- debug_set_level(adapter->hba_dbf, 3);
+ dbf->hba = zfcp_dbf_reg(dbf_name, 3, &zfcp_dbf_hba_view,
+ sizeof(struct zfcp_dbf_hba_record));
+ if (!dbf->hba)
+ goto err_out;
/* debug feature area which records SAN command failures and recovery */
sprintf(dbf_name, "zfcp_%s_san", dev_name(&adapter->ccw_device->dev));
- adapter->san_dbf = debug_register(dbf_name, dbfsize, 1,
- sizeof(struct zfcp_san_dbf_record));
- if (!adapter->san_dbf)
- goto failed;
- debug_register_view(adapter->san_dbf, &debug_hex_ascii_view);
- debug_register_view(adapter->san_dbf, &zfcp_san_dbf_view);
- debug_set_level(adapter->san_dbf, 6);
+ dbf->san = zfcp_dbf_reg(dbf_name, 6, &zfcp_dbf_san_view,
+ sizeof(struct zfcp_dbf_san_record));
+ if (!dbf->san)
+ goto err_out;
/* debug feature area which records SCSI command failures and recovery */
sprintf(dbf_name, "zfcp_%s_scsi", dev_name(&adapter->ccw_device->dev));
- adapter->scsi_dbf = debug_register(dbf_name, dbfsize, 1,
- sizeof(struct zfcp_scsi_dbf_record));
- if (!adapter->scsi_dbf)
- goto failed;
- debug_register_view(adapter->scsi_dbf, &debug_hex_ascii_view);
- debug_register_view(adapter->scsi_dbf, &zfcp_scsi_dbf_view);
- debug_set_level(adapter->scsi_dbf, 3);
+ dbf->scsi = zfcp_dbf_reg(dbf_name, 3, &zfcp_dbf_scsi_view,
+ sizeof(struct zfcp_dbf_scsi_record));
+ if (!dbf->scsi)
+ goto err_out;
+ adapter->dbf = dbf;
return 0;
- failed:
- zfcp_adapter_debug_unregister(adapter);
-
+err_out:
+ zfcp_dbf_adapter_unregister(dbf);
return -ENOMEM;
}
/**
* zfcp_adapter_debug_unregister - unregisters debug feature for an adapter
- * @adapter: pointer to adapter for which debug features should be unregistered
+ * @dbf: pointer to dbf for which debug features should be unregistered
*/
-void zfcp_adapter_debug_unregister(struct zfcp_adapter *adapter)
+void zfcp_dbf_adapter_unregister(struct zfcp_dbf *dbf)
{
- debug_unregister(adapter->scsi_dbf);
- debug_unregister(adapter->san_dbf);
- debug_unregister(adapter->hba_dbf);
- debug_unregister(adapter->rec_dbf);
- adapter->scsi_dbf = NULL;
- adapter->san_dbf = NULL;
- adapter->hba_dbf = NULL;
- adapter->rec_dbf = NULL;
+ debug_unregister(dbf->scsi);
+ debug_unregister(dbf->san);
+ debug_unregister(dbf->hba);
+ debug_unregister(dbf->rec);
+ dbf->adapter->dbf = NULL;
+ kfree(dbf);
}
+
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index a573f7344dd..6b1461e8f84 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -2,7 +2,7 @@
* This file is part of the zfcp device driver for
* FCP adapters for IBM System z9 and zSeries.
*
- * Copyright IBM Corp. 2008, 2008
+ * Copyright IBM Corp. 2008, 2009
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,7 +22,9 @@
#ifndef ZFCP_DBF_H
#define ZFCP_DBF_H
+#include "zfcp_ext.h"
#include "zfcp_fsf.h"
+#include "zfcp_def.h"
#define ZFCP_DBF_TAG_SIZE 4
#define ZFCP_DBF_ID_SIZE 7
@@ -35,13 +37,13 @@ struct zfcp_dbf_dump {
u8 data[]; /* dump data */
} __attribute__ ((packed));
-struct zfcp_rec_dbf_record_thread {
+struct zfcp_dbf_rec_record_thread {
u32 total;
u32 ready;
u32 running;
};
-struct zfcp_rec_dbf_record_target {
+struct zfcp_dbf_rec_record_target {
u64 ref;
u32 status;
u32 d_id;
@@ -50,7 +52,7 @@ struct zfcp_rec_dbf_record_target {
u32 erp_count;
};
-struct zfcp_rec_dbf_record_trigger {
+struct zfcp_dbf_rec_record_trigger {
u8 want;
u8 need;
u32 as;
@@ -62,21 +64,21 @@ struct zfcp_rec_dbf_record_trigger {
u64 fcp_lun;
};
-struct zfcp_rec_dbf_record_action {
+struct zfcp_dbf_rec_record_action {
u32 status;
u32 step;
u64 action;
u64 fsf_req;
};
-struct zfcp_rec_dbf_record {
+struct zfcp_dbf_rec_record {
u8 id;
char id2[7];
union {
- struct zfcp_rec_dbf_record_action action;
- struct zfcp_rec_dbf_record_thread thread;
- struct zfcp_rec_dbf_record_target target;
- struct zfcp_rec_dbf_record_trigger trigger;
+ struct zfcp_dbf_rec_record_action action;
+ struct zfcp_dbf_rec_record_thread thread;
+ struct zfcp_dbf_rec_record_target target;
+ struct zfcp_dbf_rec_record_trigger trigger;
} u;
};
@@ -87,7 +89,7 @@ enum {
ZFCP_REC_DBF_ID_TRIGGER,
};
-struct zfcp_hba_dbf_record_response {
+struct zfcp_dbf_hba_record_response {
u32 fsf_command;
u64 fsf_reqid;
u32 fsf_seqno;
@@ -125,7 +127,7 @@ struct zfcp_hba_dbf_record_response {
} u;
} __attribute__ ((packed));
-struct zfcp_hba_dbf_record_status {
+struct zfcp_dbf_hba_record_status {
u8 failed;
u32 status_type;
u32 status_subtype;
@@ -139,24 +141,24 @@ struct zfcp_hba_dbf_record_status {
u8 payload[ZFCP_DBF_UNSOL_PAYLOAD];
} __attribute__ ((packed));
-struct zfcp_hba_dbf_record_qdio {
+struct zfcp_dbf_hba_record_qdio {
u32 qdio_error;
u8 sbal_index;
u8 sbal_count;
} __attribute__ ((packed));
-struct zfcp_hba_dbf_record {
+struct zfcp_dbf_hba_record {
u8 tag[ZFCP_DBF_TAG_SIZE];
u8 tag2[ZFCP_DBF_TAG_SIZE];
union {
- struct zfcp_hba_dbf_record_response response;
- struct zfcp_hba_dbf_record_status status;
- struct zfcp_hba_dbf_record_qdio qdio;
+ struct zfcp_dbf_hba_record_response response;
+ struct zfcp_dbf_hba_record_status status;
+ struct zfcp_dbf_hba_record_qdio qdio;
struct fsf_bit_error_payload berr;
} u;
} __attribute__ ((packed));
-struct zfcp_san_dbf_record_ct_request {
+struct zfcp_dbf_san_record_ct_request {
u16 cmd_req_code;
u8 revision;
u8 gs_type;
@@ -166,7 +168,7 @@ struct zfcp_san_dbf_record_ct_request {
u32 len;
} __attribute__ ((packed));
-struct zfcp_san_dbf_record_ct_response {
+struct zfcp_dbf_san_record_ct_response {
u16 cmd_rsp_code;
u8 revision;
u8 reason_code;
@@ -176,27 +178,27 @@ struct zfcp_san_dbf_record_ct_response {
u32 len;
} __attribute__ ((packed));
-struct zfcp_san_dbf_record_els {
+struct zfcp_dbf_san_record_els {
u8 ls_code;
u32 len;
} __attribute__ ((packed));
-struct zfcp_san_dbf_record {
+struct zfcp_dbf_san_record {
u8 tag[ZFCP_DBF_TAG_SIZE];
u64 fsf_reqid;
u32 fsf_seqno;
u32 s_id;
u32 d_id;
union {
- struct zfcp_san_dbf_record_ct_request ct_req;
- struct zfcp_san_dbf_record_ct_response ct_resp;
- struct zfcp_san_dbf_record_els els;
+ struct zfcp_dbf_san_record_ct_request ct_req;
+ struct zfcp_dbf_san_record_ct_response ct_resp;
+ struct zfcp_dbf_san_record_els els;
} u;
#define ZFCP_DBF_SAN_MAX_PAYLOAD 1024
u8 payload[32];
} __attribute__ ((packed));
-struct zfcp_scsi_dbf_record {
+struct zfcp_dbf_scsi_record {
u8 tag[ZFCP_DBF_TAG_SIZE];
u8 tag2[ZFCP_DBF_TAG_SIZE];
u32 scsi_id;
@@ -222,4 +224,127 @@ struct zfcp_scsi_dbf_record {
u8 sns_info[ZFCP_DBF_SCSI_FCP_SNS_INFO];
} __attribute__ ((packed));
+struct zfcp_dbf {
+ debug_info_t *rec;
+ debug_info_t *hba;
+ debug_info_t *san;
+ debug_info_t *scsi;
+ spinlock_t rec_lock;
+ spinlock_t hba_lock;
+ spinlock_t san_lock;
+ spinlock_t scsi_lock;
+ struct zfcp_dbf_rec_record rec_buf;
+ struct zfcp_dbf_hba_record hba_buf;
+ struct zfcp_dbf_san_record san_buf;
+ struct zfcp_dbf_scsi_record scsi_buf;
+ struct zfcp_adapter *adapter;
+};
+
+static inline
+void zfcp_dbf_hba_fsf_resp(const char *tag2, int level,
+ struct zfcp_fsf_req *req, struct zfcp_dbf *dbf)
+{
+ if (level <= dbf->hba->level)
+ _zfcp_dbf_hba_fsf_response(tag2, level, req, dbf);
+}
+
+/**
+ * zfcp_dbf_hba_fsf_response - trace event for request completion
+ * @fsf_req: request that has been completed
+ */
+static inline void zfcp_dbf_hba_fsf_response(struct zfcp_fsf_req *req)
+{
+ struct zfcp_dbf *dbf = req->adapter->dbf;
+ struct fsf_qtcb *qtcb = req->qtcb;
+
+ if ((qtcb->prefix.prot_status != FSF_PROT_GOOD) &&
+ (qtcb->prefix.prot_status != FSF_PROT_FSF_STATUS_PRESENTED)) {
+ zfcp_dbf_hba_fsf_resp("perr", 1, req, dbf);
+
+ } else if (qtcb->header.fsf_status != FSF_GOOD) {
+ zfcp_dbf_hba_fsf_resp("ferr", 1, req, dbf);
+
+ } else if ((req->fsf_command == FSF_QTCB_OPEN_PORT_WITH_DID) ||
+ (req->fsf_command == FSF_QTCB_OPEN_LUN)) {
+ zfcp_dbf_hba_fsf_resp("open", 4, req, dbf);
+
+ } else if (qtcb->header.log_length) {
+ zfcp_dbf_hba_fsf_resp("qtcb", 5, req, dbf);
+
+ } else {
+ zfcp_dbf_hba_fsf_resp("norm", 6, req, dbf);
+ }
+ }
+
+/**
+ * zfcp_dbf_hba_fsf_unsol - trace event for an unsolicited status buffer
+ * @tag: tag indicating which kind of unsolicited status has been received
+ * @dbf: reference to dbf structure
+ * @status_buffer: buffer containing payload of unsolicited status
+ */
+static inline
+void zfcp_dbf_hba_fsf_unsol(const char *tag, struct zfcp_dbf *dbf,
+ struct fsf_status_read_buffer *buf)
+{
+ int level = 2;
+
+ if (level <= dbf->hba->level)
+ _zfcp_dbf_hba_fsf_unsol(tag, level, dbf, buf);
+}
+
+static inline
+void zfcp_dbf_scsi(const char *tag, const char *tag2, int level,
+ struct zfcp_dbf *dbf, struct scsi_cmnd *scmd,
+ struct zfcp_fsf_req *req, unsigned long old_id)
+{
+ if (level <= dbf->scsi->level)
+ _zfcp_dbf_scsi(tag, tag2, level, dbf, scmd, req, old_id);
+}
+
+/**
+ * zfcp_dbf_scsi_result - trace event for SCSI command completion
+ * @tag: tag indicating success or failure of SCSI command
+ * @level: trace level applicable for this event
+ * @adapter: adapter that has been used to issue the SCSI command
+ * @scmd: SCSI command pointer
+ * @fsf_req: request used to issue SCSI command (might be NULL)
+ */
+static inline
+void zfcp_dbf_scsi_result(const char *tag, int level, struct zfcp_dbf *dbf,
+ struct scsi_cmnd *scmd, struct zfcp_fsf_req *fsf_req)
+{
+ zfcp_dbf_scsi("rslt", tag, level, dbf, scmd, fsf_req, 0);
+}
+
+/**
+ * zfcp_dbf_scsi_abort - trace event for SCSI command abort
+ * @tag: tag indicating success or failure of abort operation
+ * @adapter: adapter thas has been used to issue SCSI command to be aborted
+ * @scmd: SCSI command to be aborted
+ * @new_req: request containing abort (might be NULL)
+ * @old_id: identifier of request containg SCSI command to be aborted
+ */
+static inline
+void zfcp_dbf_scsi_abort(const char *tag, struct zfcp_dbf *dbf,
+ struct scsi_cmnd *scmd, struct zfcp_fsf_req *new_req,
+ unsigned long old_id)
+{
+ zfcp_dbf_scsi("abrt", tag, 1, dbf, scmd, new_req, old_id);
+}
+
+/**
+ * zfcp_dbf_scsi_devreset - trace event for Logical Unit or Target Reset
+ * @tag: tag indicating success or failure of reset operation
+ * @flag: indicates type of reset (Target Reset, Logical Unit Reset)
+ * @unit: unit that needs reset
+ * @scsi_cmnd: SCSI command which caused this error recovery
+ */
+static inline
+void zfcp_dbf_scsi_devreset(const char *tag, u8 flag, struct zfcp_unit *unit,
+ struct scsi_cmnd *scsi_cmnd)
+{
+ zfcp_dbf_scsi(flag == FCP_TARGET_RESET ? "trst" : "lrst", tag, 1,
+ unit->port->adapter->dbf, scsi_cmnd, NULL, 0);
+}
+
#endif /* ZFCP_DBF_H */
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index 49d0532bca1..7da2fad8f51 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -37,10 +37,8 @@
#include <asm/debug.h>
#include <asm/ebcdic.h>
#include <asm/sysinfo.h>
-#include "zfcp_dbf.h"
#include "zfcp_fsf.h"
-
/********************* GENERAL DEFINES *********************************/
#define REQUEST_LIST_SIZE 128
@@ -75,9 +73,6 @@
/*************** FIBRE CHANNEL PROTOCOL SPECIFIC DEFINES ********************/
-/* timeout for name-server lookup (in seconds) */
-#define ZFCP_NS_GID_PN_TIMEOUT 10
-
/* task attribute values in FCP-2 FCP_CMND IU */
#define SIMPLE_Q 0
#define HEAD_OF_Q 1
@@ -224,8 +219,6 @@ struct zfcp_ls_adisc {
#define ZFCP_STATUS_ADAPTER_QDIOUP 0x00000002
#define ZFCP_STATUS_ADAPTER_XCONFIG_OK 0x00000008
#define ZFCP_STATUS_ADAPTER_HOST_CON_INIT 0x00000010
-#define ZFCP_STATUS_ADAPTER_ERP_THREAD_UP 0x00000020
-#define ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL 0x00000080
#define ZFCP_STATUS_ADAPTER_ERP_PENDING 0x00000100
#define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED 0x00000200
@@ -234,6 +227,7 @@ struct zfcp_ls_adisc {
/* remote port status */
#define ZFCP_STATUS_PORT_PHYS_OPEN 0x00000001
+#define ZFCP_STATUS_PORT_LINK_TEST 0x00000002
/* well known address (WKA) port status*/
enum zfcp_wka_status {
@@ -249,7 +243,6 @@ enum zfcp_wka_status {
/* FSF request status (this does not have a common part) */
#define ZFCP_STATUS_FSFREQ_TASK_MANAGEMENT 0x00000002
-#define ZFCP_STATUS_FSFREQ_COMPLETED 0x00000004
#define ZFCP_STATUS_FSFREQ_ERROR 0x00000008
#define ZFCP_STATUS_FSFREQ_CLEANUP 0x00000010
#define ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED 0x00000040
@@ -266,12 +259,14 @@ struct zfcp_fsf_req;
/* holds various memory pools of an adapter */
struct zfcp_adapter_mempool {
- mempool_t *fsf_req_erp;
- mempool_t *fsf_req_scsi;
- mempool_t *fsf_req_abort;
- mempool_t *fsf_req_status_read;
- mempool_t *data_status_read;
- mempool_t *data_gid_pn;
+ mempool_t *erp_req;
+ mempool_t *gid_pn_req;
+ mempool_t *scsi_req;
+ mempool_t *scsi_abort;
+ mempool_t *status_read_req;
+ mempool_t *status_read_data;
+ mempool_t *gid_pn_data;
+ mempool_t *qtcb_pool;
};
/*
@@ -305,6 +300,15 @@ struct ct_iu_gid_pn_resp {
u32 d_id;
} __attribute__ ((packed));
+struct ct_iu_gpn_ft_req {
+ struct ct_hdr header;
+ u8 flags;
+ u8 domain_id_scope;
+ u8 area_id_scope;
+ u8 fc4_type;
+} __attribute__ ((packed));
+
+
/**
* struct zfcp_send_ct - used to pass parameters to function zfcp_fsf_send_ct
* @wka_port: port where the request is sent to
@@ -312,7 +316,6 @@ struct ct_iu_gid_pn_resp {
* @resp: scatter-gather list for response
* @handler: handler function (called for response to the request)
* @handler_data: data passed to handler function
- * @timeout: FSF timeout for this request
* @completion: completion for synchronization purposes
* @status: used to pass error status to calling function
*/
@@ -322,7 +325,6 @@ struct zfcp_send_ct {
struct scatterlist *resp;
void (*handler)(unsigned long);
unsigned long handler_data;
- int timeout;
struct completion *completion;
int status;
};
@@ -420,6 +422,29 @@ struct zfcp_latencies {
spinlock_t lock;
};
+/** struct zfcp_qdio - basic QDIO data structure
+ * @resp_q: response queue
+ * @req_q: request queue
+ * @stat_lock: lock to protect req_q_util and req_q_time
+ * @req_q_lock; lock to serialize access to request queue
+ * @req_q_time: time of last fill level change
+ * @req_q_util: used for accounting
+ * @req_q_full: queue full incidents
+ * @req_q_wq: used to wait for SBAL availability
+ * @adapter: adapter used in conjunction with this QDIO structure
+ */
+struct zfcp_qdio {
+ struct zfcp_qdio_queue resp_q;
+ struct zfcp_qdio_queue req_q;
+ spinlock_t stat_lock;
+ spinlock_t req_q_lock;
+ unsigned long long req_q_time;
+ u64 req_q_util;
+ atomic_t req_q_full;
+ wait_queue_head_t req_q_wq;
+ struct zfcp_adapter *adapter;
+};
+
struct zfcp_adapter {
atomic_t refcount; /* reference count */
wait_queue_head_t remove_wq; /* can be used to wait for
@@ -428,6 +453,7 @@ struct zfcp_adapter {
u64 peer_wwpn; /* P2P peer WWPN */
u32 peer_d_id; /* P2P peer D_ID */
struct ccw_device *ccw_device; /* S/390 ccw device */
+ struct zfcp_qdio *qdio;
u32 hydra_version; /* Hydra version */
u32 fsf_lic_version;
u32 adapter_features; /* FCP channel features */
@@ -439,15 +465,7 @@ struct zfcp_adapter {
unsigned long req_no; /* unique FSF req number */
struct list_head *req_list; /* list of pending reqs */
spinlock_t req_list_lock; /* request list lock */
- struct zfcp_qdio_queue req_q; /* request queue */
- spinlock_t req_q_lock; /* for operations on queue */
- ktime_t req_q_time; /* time of last fill level change */
- u64 req_q_util; /* for accounting */
- spinlock_t qdio_stat_lock;
u32 fsf_req_seq_no; /* FSF cmnd seq number */
- wait_queue_head_t request_wq; /* can be used to wait for
- more avaliable SBALs */
- struct zfcp_qdio_queue resp_q; /* response queue */
rwlock_t abort_lock; /* Protects against SCSI
stack abort/command
completion races */
@@ -456,10 +474,9 @@ struct zfcp_adapter {
atomic_t status; /* status of this adapter */
struct list_head erp_ready_head; /* error recovery for this
adapter/devices */
+ wait_queue_head_t erp_ready_wq;
struct list_head erp_running_head;
rwlock_t erp_lock;
- struct semaphore erp_ready_sem;
- wait_queue_head_t erp_thread_wqh;
wait_queue_head_t erp_done_wqh;
struct zfcp_erp_action erp_action; /* pending error recovery */
atomic_t erp_counter;
@@ -467,27 +484,16 @@ struct zfcp_adapter {
actions */
u32 erp_low_mem_count; /* nr of erp actions waiting
for memory */
+ struct task_struct *erp_thread;
struct zfcp_wka_ports *gs; /* generic services */
- debug_info_t *rec_dbf;
- debug_info_t *hba_dbf;
- debug_info_t *san_dbf; /* debug feature areas */
- debug_info_t *scsi_dbf;
- spinlock_t rec_dbf_lock;
- spinlock_t hba_dbf_lock;
- spinlock_t san_dbf_lock;
- spinlock_t scsi_dbf_lock;
- struct zfcp_rec_dbf_record rec_dbf_buf;
- struct zfcp_hba_dbf_record hba_dbf_buf;
- struct zfcp_san_dbf_record san_dbf_buf;
- struct zfcp_scsi_dbf_record scsi_dbf_buf;
+ struct zfcp_dbf *dbf; /* debug traces */
struct zfcp_adapter_mempool pool; /* Adapter memory pools */
- struct qdio_initialize qdio_init_data; /* for qdio_establish */
struct fc_host_statistics *fc_stats;
struct fsf_qtcb_bottom_port *stats_reset_data;
unsigned long stats_reset;
struct work_struct scan_work;
struct service_level service_level;
- atomic_t qdio_outb_full; /* queue full incidents */
+ struct workqueue_struct *work_queue;
};
struct zfcp_port {
@@ -531,36 +537,64 @@ struct zfcp_unit {
struct work_struct scsi_work;
};
-/* FSF request */
+/**
+ * struct zfcp_queue_req - queue related values for a request
+ * @sbal_number: number of free SBALs
+ * @sbal_first: first SBAL for this request
+ * @sbal_last: last SBAL for this request
+ * @sbal_limit: last possible SBAL for this request
+ * @sbale_curr: current SBALE at creation of this request
+ * @sbal_response: SBAL used in interrupt
+ * @qdio_outb_usage: usage of outbound queue
+ * @qdio_inb_usage: usage of inbound queue
+ */
+struct zfcp_queue_req {
+ u8 sbal_number;
+ u8 sbal_first;
+ u8 sbal_last;
+ u8 sbal_limit;
+ u8 sbale_curr;
+ u8 sbal_response;
+ u16 qdio_outb_usage;
+ u16 qdio_inb_usage;
+};
+
+/**
+ * struct zfcp_fsf_req - basic FSF request structure
+ * @list: list of FSF requests
+ * @req_id: unique request ID
+ * @adapter: adapter this request belongs to
+ * @queue_req: queue related values
+ * @completion: used to signal the completion of the request
+ * @status: status of the request
+ * @fsf_command: FSF command issued
+ * @qtcb: associated QTCB
+ * @seq_no: sequence number of this request
+ * @data: private data
+ * @timer: timer data of this request
+ * @erp_action: reference to erp action if request issued on behalf of ERP
+ * @pool: reference to memory pool if used for this request
+ * @issued: time when request was send (STCK)
+ * @unit: reference to unit if this request is a SCSI request
+ * @handler: handler which should be called to process response
+ */
struct zfcp_fsf_req {
- struct list_head list; /* list of FSF requests */
- unsigned long req_id; /* unique request ID */
- struct zfcp_adapter *adapter; /* adapter request belongs to */
- u8 sbal_number; /* nr of SBALs free for use */
- u8 sbal_first; /* first SBAL for this request */
- u8 sbal_last; /* last SBAL for this request */
- u8 sbal_limit; /* last possible SBAL for
- this reuest */
- u8 sbale_curr; /* current SBALE during creation
- of request */
- u8 sbal_response; /* SBAL used in interrupt */
- wait_queue_head_t completion_wq; /* can be used by a routine
- to wait for completion */
- u32 status; /* status of this request */
- u32 fsf_command; /* FSF Command copy */
- struct fsf_qtcb *qtcb; /* address of associated QTCB */
- u32 seq_no; /* Sequence number of request */
- void *data; /* private data of request */
- struct timer_list timer; /* used for erp or scsi er */
- struct zfcp_erp_action *erp_action; /* used if this request is
- issued on behalf of erp */
- mempool_t *pool; /* used if request was alloacted
- from emergency pool */
- unsigned long long issued; /* request sent time (STCK) */
- struct zfcp_unit *unit;
+ struct list_head list;
+ unsigned long req_id;
+ struct zfcp_adapter *adapter;
+ struct zfcp_queue_req queue_req;
+ struct completion completion;
+ u32 status;
+ u32 fsf_command;
+ struct fsf_qtcb *qtcb;
+ u32 seq_no;
+ void *data;
+ struct timer_list timer;
+ struct zfcp_erp_action *erp_action;
+ mempool_t *pool;
+ unsigned long long issued;
+ struct zfcp_unit *unit;
void (*handler)(struct zfcp_fsf_req *);
- u16 qdio_outb_usage;/* usage of outbound queue */
- u16 qdio_inb_usage; /* usage of inbound queue */
};
/* driver data */
@@ -570,18 +604,11 @@ struct zfcp_data {
rwlock_t config_lock; /* serialises changes
to adapter/port/unit
lists */
- struct semaphore config_sema; /* serialises configuration
- changes */
- struct kmem_cache *fsf_req_qtcb_cache;
+ struct mutex config_mutex;
+ struct kmem_cache *gpn_ft_cache;
+ struct kmem_cache *qtcb_cache;
struct kmem_cache *sr_buffer_cache;
struct kmem_cache *gid_pn_cache;
- struct workqueue_struct *work_queue;
-};
-
-/* struct used by memory pools for fsf_requests */
-struct zfcp_fsf_req_qtcb {
- struct zfcp_fsf_req fsf_req;
- struct fsf_qtcb qtcb;
};
/********************** ZFCP SPECIFIC DEFINES ********************************/
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index c75d6f35cb5..73d366ba31e 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -9,6 +9,7 @@
#define KMSG_COMPONENT "zfcp"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#include <linux/kthread.h>
#include "zfcp_ext.h"
#define ZFCP_MAX_ERPS 3
@@ -26,7 +27,6 @@ enum zfcp_erp_steps {
ZFCP_ERP_STEP_FSF_XCONFIG = 0x0001,
ZFCP_ERP_STEP_PHYS_PORT_CLOSING = 0x0010,
ZFCP_ERP_STEP_PORT_CLOSING = 0x0100,
- ZFCP_ERP_STEP_NAMESERVER_LOOKUP = 0x0400,
ZFCP_ERP_STEP_PORT_OPENING = 0x0800,
ZFCP_ERP_STEP_UNIT_CLOSING = 0x1000,
ZFCP_ERP_STEP_UNIT_OPENING = 0x2000,
@@ -75,9 +75,9 @@ static void zfcp_erp_action_ready(struct zfcp_erp_action *act)
struct zfcp_adapter *adapter = act->adapter;
list_move(&act->list, &act->adapter->erp_ready_head);
- zfcp_rec_dbf_event_action("erardy1", act);
- up(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread("erardy2", adapter);
+ zfcp_dbf_rec_action("erardy1", act);
+ wake_up(&adapter->erp_ready_wq);
+ zfcp_dbf_rec_thread("erardy2", adapter->dbf);
}
static void zfcp_erp_action_dismiss(struct zfcp_erp_action *act)
@@ -150,6 +150,9 @@ static int zfcp_erp_required_act(int want, struct zfcp_adapter *adapter,
a_status = atomic_read(&adapter->status);
if (a_status & ZFCP_STATUS_COMMON_ERP_INUSE)
return 0;
+ if (!(a_status & ZFCP_STATUS_COMMON_RUNNING) &&
+ !(a_status & ZFCP_STATUS_COMMON_OPEN))
+ return 0; /* shutdown requested for closed adapter */
}
return need;
@@ -213,8 +216,7 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
int retval = 1, need;
struct zfcp_erp_action *act = NULL;
- if (!(atomic_read(&adapter->status) &
- ZFCP_STATUS_ADAPTER_ERP_THREAD_UP))
+ if (!adapter->erp_thread)
return -EIO;
need = zfcp_erp_required_act(want, adapter, port, unit);
@@ -227,12 +229,11 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
goto out;
++adapter->erp_total_count;
list_add_tail(&act->list, &adapter->erp_ready_head);
- up(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread("eracte1", adapter);
+ wake_up(&adapter->erp_ready_wq);
+ zfcp_dbf_rec_thread("eracte1", adapter->dbf);
retval = 0;
out:
- zfcp_rec_dbf_event_trigger(id, ref, want, need, act,
- adapter, port, unit);
+ zfcp_dbf_rec_trigger(id, ref, want, need, act, adapter, port, unit);
return retval;
}
@@ -443,28 +444,28 @@ static int status_change_clear(unsigned long mask, atomic_t *status)
static void zfcp_erp_adapter_unblock(struct zfcp_adapter *adapter)
{
if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status))
- zfcp_rec_dbf_event_adapter("eraubl1", NULL, adapter);
+ zfcp_dbf_rec_adapter("eraubl1", NULL, adapter->dbf);
atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &adapter->status);
}
static void zfcp_erp_port_unblock(struct zfcp_port *port)
{
if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status))
- zfcp_rec_dbf_event_port("erpubl1", NULL, port);
+ zfcp_dbf_rec_port("erpubl1", NULL, port);
atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &port->status);
}
static void zfcp_erp_unit_unblock(struct zfcp_unit *unit)
{
if (status_change_set(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status))
- zfcp_rec_dbf_event_unit("eruubl1", NULL, unit);
+ zfcp_dbf_rec_unit("eruubl1", NULL, unit);
atomic_set_mask(ZFCP_STATUS_COMMON_UNBLOCKED, &unit->status);
}
static void zfcp_erp_action_to_running(struct zfcp_erp_action *erp_action)
{
list_move(&erp_action->list, &erp_action->adapter->erp_running_head);
- zfcp_rec_dbf_event_action("erator1", erp_action);
+ zfcp_dbf_rec_action("erator1", erp_action);
}
static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act)
@@ -480,13 +481,12 @@ static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act)
if (act->status & (ZFCP_STATUS_ERP_DISMISSED |
ZFCP_STATUS_ERP_TIMEDOUT)) {
act->fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
- zfcp_rec_dbf_event_action("erscf_1", act);
+ zfcp_dbf_rec_action("erscf_1", act);
act->fsf_req->erp_action = NULL;
}
if (act->status & ZFCP_STATUS_ERP_TIMEDOUT)
- zfcp_rec_dbf_event_action("erscf_2", act);
- if (act->fsf_req->status & (ZFCP_STATUS_FSFREQ_COMPLETED |
- ZFCP_STATUS_FSFREQ_DISMISSED))
+ zfcp_dbf_rec_action("erscf_2", act);
+ if (act->fsf_req->status & ZFCP_STATUS_FSFREQ_DISMISSED)
act->fsf_req = NULL;
} else
act->fsf_req = NULL;
@@ -604,9 +604,11 @@ static void zfcp_erp_wakeup(struct zfcp_adapter *adapter)
static int zfcp_erp_adapter_strategy_open_qdio(struct zfcp_erp_action *act)
{
- if (zfcp_qdio_open(act->adapter))
+ struct zfcp_qdio *qdio = act->adapter->qdio;
+
+ if (zfcp_qdio_open(qdio))
return ZFCP_ERP_FAILED;
- init_waitqueue_head(&act->adapter->request_wq);
+ init_waitqueue_head(&qdio->req_q_wq);
atomic_set_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &act->adapter->status);
return ZFCP_ERP_SUCCEEDED;
}
@@ -641,9 +643,10 @@ static int zfcp_erp_adapter_strat_fsf_xconf(struct zfcp_erp_action *erp_action)
return ZFCP_ERP_FAILED;
}
- zfcp_rec_dbf_event_thread_lock("erasfx1", adapter);
- down(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread_lock("erasfx2", adapter);
+ zfcp_dbf_rec_thread_lock("erasfx1", adapter->dbf);
+ wait_event(adapter->erp_ready_wq,
+ !list_empty(&adapter->erp_ready_head));
+ zfcp_dbf_rec_thread_lock("erasfx2", adapter->dbf);
if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT)
break;
@@ -682,9 +685,10 @@ static int zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *act)
if (ret)
return ZFCP_ERP_FAILED;
- zfcp_rec_dbf_event_thread_lock("erasox1", adapter);
- down(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread_lock("erasox2", adapter);
+ zfcp_dbf_rec_thread_lock("erasox1", adapter->dbf);
+ wait_event(adapter->erp_ready_wq,
+ !list_empty(&adapter->erp_ready_head));
+ zfcp_dbf_rec_thread_lock("erasox2", adapter->dbf);
if (act->status & ZFCP_STATUS_ERP_TIMEDOUT)
return ZFCP_ERP_FAILED;
@@ -711,10 +715,10 @@ static void zfcp_erp_adapter_strategy_close(struct zfcp_erp_action *act)
struct zfcp_adapter *adapter = act->adapter;
/* close queues to ensure that buffers are not accessed by adapter */
- zfcp_qdio_close(adapter);
+ zfcp_qdio_close(adapter->qdio);
zfcp_fsf_req_dismiss_all(adapter);
adapter->fsf_req_seq_no = 0;
- zfcp_fc_wka_port_force_offline(&adapter->gs->ds);
+ zfcp_fc_wka_ports_force_offline(adapter->gs);
/* all ports and units are closed */
zfcp_erp_modify_adapter_status(adapter, "erascl1", NULL,
ZFCP_STATUS_COMMON_OPEN, ZFCP_CLEAR);
@@ -841,27 +845,6 @@ static int zfcp_erp_open_ptp_port(struct zfcp_erp_action *act)
return zfcp_erp_port_strategy_open_port(act);
}
-void zfcp_erp_port_strategy_open_lookup(struct work_struct *work)
-{
- int retval;
- struct zfcp_port *port = container_of(work, struct zfcp_port,
- gid_pn_work);
-
- retval = zfcp_fc_ns_gid_pn(&port->erp_action);
- if (!retval) {
- port->erp_action.step = ZFCP_ERP_STEP_NAMESERVER_LOOKUP;
- goto out;
- }
- if (retval == -ENOMEM) {
- zfcp_erp_notify(&port->erp_action, ZFCP_STATUS_ERP_LOWMEM);
- goto out;
- }
- /* all other error condtions */
- zfcp_erp_notify(&port->erp_action, 0);
-out:
- zfcp_port_put(port);
-}
-
static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
{
struct zfcp_adapter *adapter = act->adapter;
@@ -876,15 +859,11 @@ static int zfcp_erp_port_strategy_open_common(struct zfcp_erp_action *act)
return zfcp_erp_open_ptp_port(act);
if (!port->d_id) {
zfcp_port_get(port);
- if (!queue_work(zfcp_data.work_queue,
+ if (!queue_work(adapter->work_queue,
&port->gid_pn_work))
zfcp_port_put(port);
- return ZFCP_ERP_CONTINUES;
+ return ZFCP_ERP_EXIT;
}
- /* fall through */
- case ZFCP_ERP_STEP_NAMESERVER_LOOKUP:
- if (!port->d_id)
- return ZFCP_ERP_FAILED;
return zfcp_erp_port_strategy_open_port(act);
case ZFCP_ERP_STEP_PORT_OPENING:
@@ -1163,7 +1142,7 @@ static void zfcp_erp_action_dequeue(struct zfcp_erp_action *erp_action)
}
list_del(&erp_action->list);
- zfcp_rec_dbf_event_action("eractd1", erp_action);
+ zfcp_dbf_rec_action("eractd1", erp_action);
switch (erp_action->action) {
case ZFCP_ERP_ACTION_REOPEN_UNIT:
@@ -1311,20 +1290,16 @@ static int zfcp_erp_thread(void *data)
struct list_head *next;
struct zfcp_erp_action *act;
unsigned long flags;
- int ignore;
-
- daemonize("zfcperp%s", dev_name(&adapter->ccw_device->dev));
- /* Block all signals */
- siginitsetinv(&current->blocked, 0);
- atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
- wake_up(&adapter->erp_thread_wqh);
- while (!(atomic_read(&adapter->status) &
- ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL)) {
+ for (;;) {
+ zfcp_dbf_rec_thread_lock("erthrd1", adapter->dbf);
+ wait_event_interruptible(adapter->erp_ready_wq,
+ !list_empty(&adapter->erp_ready_head) ||
+ kthread_should_stop());
+ zfcp_dbf_rec_thread_lock("erthrd2", adapter->dbf);
- zfcp_rec_dbf_event_thread_lock("erthrd1", adapter);
- ignore = down_interruptible(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread_lock("erthrd2", adapter);
+ if (kthread_should_stop())
+ break;
write_lock_irqsave(&adapter->erp_lock, flags);
next = adapter->erp_ready_head.next;
@@ -1339,9 +1314,6 @@ static int zfcp_erp_thread(void *data)
}
}
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
- wake_up(&adapter->erp_thread_wqh);
-
return 0;
}
@@ -1353,18 +1325,17 @@ static int zfcp_erp_thread(void *data)
*/
int zfcp_erp_thread_setup(struct zfcp_adapter *adapter)
{
- int retval;
+ struct task_struct *thread;
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
- retval = kernel_thread(zfcp_erp_thread, adapter, SIGCHLD);
- if (retval < 0) {
+ thread = kthread_run(zfcp_erp_thread, adapter, "zfcperp%s",
+ dev_name(&adapter->ccw_device->dev));
+ if (IS_ERR(thread)) {
dev_err(&adapter->ccw_device->dev,
"Creating an ERP thread for the FCP device failed.\n");
- return retval;
+ return PTR_ERR(thread);
}
- wait_event(adapter->erp_thread_wqh,
- atomic_read(&adapter->status) &
- ZFCP_STATUS_ADAPTER_ERP_THREAD_UP);
+
+ adapter->erp_thread = thread;
return 0;
}
@@ -1379,16 +1350,10 @@ int zfcp_erp_thread_setup(struct zfcp_adapter *adapter)
*/
void zfcp_erp_thread_kill(struct zfcp_adapter *adapter)
{
- atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
- up(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread_lock("erthrk1", adapter);
-
- wait_event(adapter->erp_thread_wqh,
- !(atomic_read(&adapter->status) &
- ZFCP_STATUS_ADAPTER_ERP_THREAD_UP));
-
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL,
- &adapter->status);
+ kthread_stop(adapter->erp_thread);
+ adapter->erp_thread = NULL;
+ WARN_ON(!list_empty(&adapter->erp_ready_head));
+ WARN_ON(!list_empty(&adapter->erp_running_head));
}
/**
@@ -1456,11 +1421,11 @@ void zfcp_erp_modify_adapter_status(struct zfcp_adapter *adapter, char *id,
if (set_or_clear == ZFCP_SET) {
if (status_change_set(mask, &adapter->status))
- zfcp_rec_dbf_event_adapter(id, ref, adapter);
+ zfcp_dbf_rec_adapter(id, ref, adapter->dbf);
atomic_set_mask(mask, &adapter->status);
} else {
if (status_change_clear(mask, &adapter->status))
- zfcp_rec_dbf_event_adapter(id, ref, adapter);
+ zfcp_dbf_rec_adapter(id, ref, adapter->dbf);
atomic_clear_mask(mask, &adapter->status);
if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
atomic_set(&adapter->erp_counter, 0);
@@ -1490,11 +1455,11 @@ void zfcp_erp_modify_port_status(struct zfcp_port *port, char *id, void *ref,
if (set_or_clear == ZFCP_SET) {
if (status_change_set(mask, &port->status))
- zfcp_rec_dbf_event_port(id, ref, port);
+ zfcp_dbf_rec_port(id, ref, port);
atomic_set_mask(mask, &port->status);
} else {
if (status_change_clear(mask, &port->status))
- zfcp_rec_dbf_event_port(id, ref, port);
+ zfcp_dbf_rec_port(id, ref, port);
atomic_clear_mask(mask, &port->status);
if (mask & ZFCP_STATUS_COMMON_ERP_FAILED)
atomic_set(&port->erp_counter, 0);
@@ -1519,11 +1484,11 @@ void zfcp_erp_modify_unit_status(struct zfcp_unit *unit, char *id, void *ref,
{
if (set_or_clear == ZFCP_SET) {
if (status_change_set(mask, &unit->status))
- zfcp_rec_dbf_event_unit(id, ref, unit);
+ zfcp_dbf_rec_unit(id, ref, unit);
atomic_set_mask(mask, &unit->status);
} else {
if (status_change_clear(mask, &unit->status))
- zfcp_rec_dbf_event_unit(id, ref, unit);
+ zfcp_dbf_rec_unit(id, ref, unit);
atomic_clear_mask(mask, &unit->status);
if (mask & ZFCP_STATUS_COMMON_ERP_FAILED) {
atomic_set(&unit->erp_counter, 0);
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 3044c601030..36935bc0818 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -34,37 +34,31 @@ extern struct zfcp_adapter *zfcp_get_adapter_by_busid(char *);
extern struct miscdevice zfcp_cfdc_misc;
/* zfcp_dbf.c */
-extern int zfcp_adapter_debug_register(struct zfcp_adapter *);
-extern void zfcp_adapter_debug_unregister(struct zfcp_adapter *);
-extern void zfcp_rec_dbf_event_thread(char *, struct zfcp_adapter *);
-extern void zfcp_rec_dbf_event_thread_lock(char *, struct zfcp_adapter *);
-extern void zfcp_rec_dbf_event_adapter(char *, void *, struct zfcp_adapter *);
-extern void zfcp_rec_dbf_event_port(char *, void *, struct zfcp_port *);
-extern void zfcp_rec_dbf_event_unit(char *, void *, struct zfcp_unit *);
-extern void zfcp_rec_dbf_event_trigger(char *, void *, u8, u8, void *,
- struct zfcp_adapter *,
- struct zfcp_port *, struct zfcp_unit *);
-extern void zfcp_rec_dbf_event_action(char *, struct zfcp_erp_action *);
-extern void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *);
-extern void zfcp_hba_dbf_event_fsf_unsol(const char *, struct zfcp_adapter *,
- struct fsf_status_read_buffer *);
-extern void zfcp_hba_dbf_event_qdio(struct zfcp_adapter *, unsigned int, int,
- int);
-extern void zfcp_hba_dbf_event_berr(struct zfcp_adapter *,
- struct zfcp_fsf_req *);
-extern void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *);
-extern void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *);
-extern void zfcp_san_dbf_event_els_request(struct zfcp_fsf_req *);
-extern void zfcp_san_dbf_event_els_response(struct zfcp_fsf_req *);
-extern void zfcp_san_dbf_event_incoming_els(struct zfcp_fsf_req *);
-extern void zfcp_scsi_dbf_event_result(const char *, int, struct zfcp_adapter *,
- struct scsi_cmnd *,
- struct zfcp_fsf_req *);
-extern void zfcp_scsi_dbf_event_abort(const char *, struct zfcp_adapter *,
- struct scsi_cmnd *, struct zfcp_fsf_req *,
- unsigned long);
-extern void zfcp_scsi_dbf_event_devreset(const char *, u8, struct zfcp_unit *,
- struct scsi_cmnd *);
+extern int zfcp_dbf_adapter_register(struct zfcp_adapter *);
+extern void zfcp_dbf_adapter_unregister(struct zfcp_dbf *);
+extern void zfcp_dbf_rec_thread(char *, struct zfcp_dbf *);
+extern void zfcp_dbf_rec_thread_lock(char *, struct zfcp_dbf *);
+extern void zfcp_dbf_rec_adapter(char *, void *, struct zfcp_dbf *);
+extern void zfcp_dbf_rec_port(char *, void *, struct zfcp_port *);
+extern void zfcp_dbf_rec_unit(char *, void *, struct zfcp_unit *);
+extern void zfcp_dbf_rec_trigger(char *, void *, u8, u8, void *,
+ struct zfcp_adapter *, struct zfcp_port *,
+ struct zfcp_unit *);
+extern void zfcp_dbf_rec_action(char *, struct zfcp_erp_action *);
+extern void _zfcp_dbf_hba_fsf_response(const char *, int, struct zfcp_fsf_req *,
+ struct zfcp_dbf *);
+extern void _zfcp_dbf_hba_fsf_unsol(const char *, int level, struct zfcp_dbf *,
+ struct fsf_status_read_buffer *);
+extern void zfcp_dbf_hba_qdio(struct zfcp_dbf *, unsigned int, int, int);
+extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *);
+extern void zfcp_dbf_san_ct_request(struct zfcp_fsf_req *);
+extern void zfcp_dbf_san_ct_response(struct zfcp_fsf_req *);
+extern void zfcp_dbf_san_els_request(struct zfcp_fsf_req *);
+extern void zfcp_dbf_san_els_response(struct zfcp_fsf_req *);
+extern void zfcp_dbf_san_incoming_els(struct zfcp_fsf_req *);
+extern void _zfcp_dbf_scsi(const char *, const char *, int, struct zfcp_dbf *,
+ struct scsi_cmnd *, struct zfcp_fsf_req *,
+ unsigned long);
/* zfcp_erp.c */
extern void zfcp_erp_modify_adapter_status(struct zfcp_adapter *, char *,
@@ -96,22 +90,20 @@ extern void zfcp_erp_unit_access_denied(struct zfcp_unit *, char *, void *);
extern void zfcp_erp_adapter_access_changed(struct zfcp_adapter *, char *,
void *);
extern void zfcp_erp_timeout_handler(unsigned long);
-extern void zfcp_erp_port_strategy_open_lookup(struct work_struct *);
/* zfcp_fc.c */
-extern int zfcp_scan_ports(struct zfcp_adapter *);
-extern void _zfcp_scan_ports_later(struct work_struct *);
+extern int zfcp_fc_scan_ports(struct zfcp_adapter *);
+extern void _zfcp_fc_scan_ports_later(struct work_struct *);
extern void zfcp_fc_incoming_els(struct zfcp_fsf_req *);
-extern int zfcp_fc_ns_gid_pn(struct zfcp_erp_action *);
+extern void zfcp_fc_port_did_lookup(struct work_struct *);
extern void zfcp_fc_plogi_evaluate(struct zfcp_port *, struct fsf_plogi *);
-extern void zfcp_test_link(struct zfcp_port *);
+extern void zfcp_fc_test_link(struct zfcp_port *);
extern void zfcp_fc_link_test_work(struct work_struct *);
-extern void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *);
-extern void zfcp_fc_wka_ports_init(struct zfcp_adapter *);
+extern void zfcp_fc_wka_ports_force_offline(struct zfcp_wka_ports *);
+extern int zfcp_fc_gs_setup(struct zfcp_adapter *);
+extern void zfcp_fc_gs_destroy(struct zfcp_adapter *);
extern int zfcp_fc_execute_els_fc_job(struct fc_bsg_job *);
extern int zfcp_fc_execute_ct_fc_job(struct fc_bsg_job *);
-extern void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *);
-
/* zfcp_fsf.c */
extern int zfcp_fsf_open_port(struct zfcp_erp_action *);
@@ -122,37 +114,39 @@ extern int zfcp_fsf_close_physical_port(struct zfcp_erp_action *);
extern int zfcp_fsf_open_unit(struct zfcp_erp_action *);
extern int zfcp_fsf_close_unit(struct zfcp_erp_action *);
extern int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *);
-extern int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *,
+extern int zfcp_fsf_exchange_config_data_sync(struct zfcp_qdio *,
struct fsf_qtcb_bottom_config *);
extern int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *);
-extern int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *,
+extern int zfcp_fsf_exchange_port_data_sync(struct zfcp_qdio *,
struct fsf_qtcb_bottom_port *);
extern struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *,
struct zfcp_fsf_cfdc *);
extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
-extern int zfcp_fsf_status_read(struct zfcp_adapter *);
+extern int zfcp_fsf_status_read(struct zfcp_qdio *);
extern int zfcp_status_read_refill(struct zfcp_adapter *adapter);
-extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
- struct zfcp_erp_action *);
+extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *);
extern int zfcp_fsf_send_els(struct zfcp_send_els *);
extern int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *,
struct scsi_cmnd *);
-extern void zfcp_fsf_req_complete(struct zfcp_fsf_req *);
extern void zfcp_fsf_req_free(struct zfcp_fsf_req *);
extern struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *, u8);
extern struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long,
struct zfcp_unit *);
+extern void zfcp_fsf_reqid_check(struct zfcp_qdio *, int);
/* zfcp_qdio.c */
-extern int zfcp_qdio_allocate(struct zfcp_adapter *);
-extern void zfcp_qdio_free(struct zfcp_adapter *);
-extern int zfcp_qdio_send(struct zfcp_fsf_req *);
-extern struct qdio_buffer_element *zfcp_qdio_sbale_req(struct zfcp_fsf_req *);
-extern struct qdio_buffer_element *zfcp_qdio_sbale_curr(struct zfcp_fsf_req *);
-extern int zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *, unsigned long,
+extern int zfcp_qdio_setup(struct zfcp_adapter *);
+extern void zfcp_qdio_destroy(struct zfcp_qdio *);
+extern int zfcp_qdio_send(struct zfcp_qdio *, struct zfcp_queue_req *);
+extern struct qdio_buffer_element
+ *zfcp_qdio_sbale_req(struct zfcp_qdio *, struct zfcp_queue_req *);
+extern struct qdio_buffer_element
+ *zfcp_qdio_sbale_curr(struct zfcp_qdio *, struct zfcp_queue_req *);
+extern int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *,
+ struct zfcp_queue_req *, unsigned long,
struct scatterlist *, int);
-extern int zfcp_qdio_open(struct zfcp_adapter *);
-extern void zfcp_qdio_close(struct zfcp_adapter *);
+extern int zfcp_qdio_open(struct zfcp_qdio *);
+extern void zfcp_qdio_close(struct zfcp_qdio *);
/* zfcp_scsi.c */
extern struct zfcp_data zfcp_data;
diff --git a/drivers/s390/scsi/zfcp_fc.c b/drivers/s390/scsi/zfcp_fc.c
index 47daebfa7e5..722f22de875 100644
--- a/drivers/s390/scsi/zfcp_fc.c
+++ b/drivers/s390/scsi/zfcp_fc.c
@@ -25,14 +25,6 @@ static u32 rscn_range_mask[] = {
[RSCN_FABRIC_ADDRESS] = 0x000000,
};
-struct ct_iu_gpn_ft_req {
- struct ct_hdr header;
- u8 flags;
- u8 domain_id_scope;
- u8 area_id_scope;
- u8 fc4_type;
-} __attribute__ ((packed));
-
struct gpn_ft_resp_acc {
u8 control;
u8 port_id[3];
@@ -65,7 +57,7 @@ struct zfcp_fc_ns_handler_data {
unsigned long handler_data;
};
-static int zfcp_wka_port_get(struct zfcp_wka_port *wka_port)
+static int zfcp_fc_wka_port_get(struct zfcp_wka_port *wka_port)
{
if (mutex_lock_interruptible(&wka_port->mutex))
return -ERESTARTSYS;
@@ -90,7 +82,7 @@ static int zfcp_wka_port_get(struct zfcp_wka_port *wka_port)
return -EIO;
}
-static void zfcp_wka_port_offline(struct work_struct *work)
+static void zfcp_fc_wka_port_offline(struct work_struct *work)
{
struct delayed_work *dw = to_delayed_work(work);
struct zfcp_wka_port *wka_port =
@@ -110,7 +102,7 @@ out:
mutex_unlock(&wka_port->mutex);
}
-static void zfcp_wka_port_put(struct zfcp_wka_port *wka_port)
+static void zfcp_fc_wka_port_put(struct zfcp_wka_port *wka_port)
{
if (atomic_dec_return(&wka_port->refcount) != 0)
return;
@@ -129,10 +121,10 @@ static void zfcp_fc_wka_port_init(struct zfcp_wka_port *wka_port, u32 d_id,
wka_port->status = ZFCP_WKA_PORT_OFFLINE;
atomic_set(&wka_port->refcount, 0);
mutex_init(&wka_port->mutex);
- INIT_DELAYED_WORK(&wka_port->work, zfcp_wka_port_offline);
+ INIT_DELAYED_WORK(&wka_port->work, zfcp_fc_wka_port_offline);
}
-void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *wka)
+static void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *wka)
{
cancel_delayed_work_sync(&wka->work);
mutex_lock(&wka->mutex);
@@ -140,15 +132,13 @@ void zfcp_fc_wka_port_force_offline(struct zfcp_wka_port *wka)
mutex_unlock(&wka->mutex);
}
-void zfcp_fc_wka_ports_init(struct zfcp_adapter *adapter)
+void zfcp_fc_wka_ports_force_offline(struct zfcp_wka_ports *gs)
{
- struct zfcp_wka_ports *gs = adapter->gs;
-
- zfcp_fc_wka_port_init(&gs->ms, FC_FID_MGMT_SERV, adapter);
- zfcp_fc_wka_port_init(&gs->ts, FC_FID_TIME_SERV, adapter);
- zfcp_fc_wka_port_init(&gs->ds, FC_FID_DIR_SERV, adapter);
- zfcp_fc_wka_port_init(&gs->as, FC_FID_ALIASES, adapter);
- zfcp_fc_wka_port_init(&gs->ks, FC_FID_SEC_KEY, adapter);
+ zfcp_fc_wka_port_force_offline(&gs->ms);
+ zfcp_fc_wka_port_force_offline(&gs->ts);
+ zfcp_fc_wka_port_force_offline(&gs->ds);
+ zfcp_fc_wka_port_force_offline(&gs->as);
+ zfcp_fc_wka_port_force_offline(&gs->ks);
}
static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
@@ -160,7 +150,7 @@ static void _zfcp_fc_incoming_rscn(struct zfcp_fsf_req *fsf_req, u32 range,
read_lock_irqsave(&zfcp_data.config_lock, flags);
list_for_each_entry(port, &fsf_req->adapter->port_list_head, list) {
if ((port->d_id & range) == (elem->nport_did & range))
- zfcp_test_link(port);
+ zfcp_fc_test_link(port);
if (!port->d_id)
zfcp_erp_port_reopen(port,
ZFCP_STATUS_COMMON_ERP_FAILED,
@@ -241,7 +231,7 @@ void zfcp_fc_incoming_els(struct zfcp_fsf_req *fsf_req)
(struct fsf_status_read_buffer *) fsf_req->data;
unsigned int els_type = status_buffer->payload.data[0];
- zfcp_san_dbf_event_incoming_els(fsf_req);
+ zfcp_dbf_san_incoming_els(fsf_req);
if (els_type == LS_PLOGI)
zfcp_fc_incoming_plogi(fsf_req);
else if (els_type == LS_LOGO)
@@ -281,19 +271,18 @@ static void zfcp_fc_ns_gid_pn_eval(unsigned long data)
port->d_id = ct_iu_resp->d_id & ZFCP_DID_MASK;
}
-int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action,
+static int zfcp_fc_ns_gid_pn_request(struct zfcp_port *port,
struct zfcp_gid_pn_data *gid_pn)
{
- struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_adapter *adapter = port->adapter;
struct zfcp_fc_ns_handler_data compl_rec;
int ret;
/* setup parameters for send generic command */
- gid_pn->port = erp_action->port;
+ gid_pn->port = port;
gid_pn->ct.wka_port = &adapter->gs->ds;
gid_pn->ct.handler = zfcp_fc_ns_handler;
gid_pn->ct.handler_data = (unsigned long) &compl_rec;
- gid_pn->ct.timeout = ZFCP_NS_GID_PN_TIMEOUT;
gid_pn->ct.req = &gid_pn->req;
gid_pn->ct.resp = &gid_pn->resp;
sg_init_one(&gid_pn->req, &gid_pn->ct_iu_req,
@@ -308,13 +297,12 @@ int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action,
gid_pn->ct_iu_req.header.options = ZFCP_CT_SYNCHRONOUS;
gid_pn->ct_iu_req.header.cmd_rsp_code = ZFCP_CT_GID_PN;
gid_pn->ct_iu_req.header.max_res_size = ZFCP_CT_SIZE_ONE_PAGE / 4;
- gid_pn->ct_iu_req.wwpn = erp_action->port->wwpn;
+ gid_pn->ct_iu_req.wwpn = port->wwpn;
init_completion(&compl_rec.done);
compl_rec.handler = zfcp_fc_ns_gid_pn_eval;
compl_rec.handler_data = (unsigned long) gid_pn;
- ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.fsf_req_erp,
- erp_action);
+ ret = zfcp_fsf_send_ct(&gid_pn->ct, adapter->pool.gid_pn_req);
if (!ret)
wait_for_completion(&compl_rec.done);
return ret;
@@ -322,33 +310,56 @@ int static zfcp_fc_ns_gid_pn_request(struct zfcp_erp_action *erp_action,
/**
* zfcp_fc_ns_gid_pn_request - initiate GID_PN nameserver request
- * @erp_action: pointer to zfcp_erp_action where GID_PN request is needed
+ * @port: port where GID_PN request is needed
* return: -ENOMEM on error, 0 otherwise
*/
-int zfcp_fc_ns_gid_pn(struct zfcp_erp_action *erp_action)
+static int zfcp_fc_ns_gid_pn(struct zfcp_port *port)
{
int ret;
struct zfcp_gid_pn_data *gid_pn;
- struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_adapter *adapter = port->adapter;
- gid_pn = mempool_alloc(adapter->pool.data_gid_pn, GFP_ATOMIC);
+ gid_pn = mempool_alloc(adapter->pool.gid_pn_data, GFP_ATOMIC);
if (!gid_pn)
return -ENOMEM;
memset(gid_pn, 0, sizeof(*gid_pn));
- ret = zfcp_wka_port_get(&adapter->gs->ds);
+ ret = zfcp_fc_wka_port_get(&adapter->gs->ds);
if (ret)
goto out;
- ret = zfcp_fc_ns_gid_pn_request(erp_action, gid_pn);
+ ret = zfcp_fc_ns_gid_pn_request(port, gid_pn);
- zfcp_wka_port_put(&adapter->gs->ds);
+ zfcp_fc_wka_port_put(&adapter->gs->ds);
out:
- mempool_free(gid_pn, adapter->pool.data_gid_pn);
+ mempool_free(gid_pn, adapter->pool.gid_pn_data);
return ret;
}
+void zfcp_fc_port_did_lookup(struct work_struct *work)
+{
+ int ret;
+ struct zfcp_port *port = container_of(work, struct zfcp_port,
+ gid_pn_work);
+
+ ret = zfcp_fc_ns_gid_pn(port);
+ if (ret) {
+ /* could not issue gid_pn for some reason */
+ zfcp_erp_adapter_reopen(port->adapter, 0, "fcgpn_1", NULL);
+ goto out;
+ }
+
+ if (!port->d_id) {
+ zfcp_erp_port_failed(port, "fcgpn_2", NULL);
+ goto out;
+ }
+
+ zfcp_erp_port_reopen(port, 0, "fcgpn_3", NULL);
+out:
+ zfcp_port_put(port);
+}
+
/**
* zfcp_fc_plogi_evaluate - evaluate PLOGI playload
* @port: zfcp_port structure
@@ -404,6 +415,7 @@ static void zfcp_fc_adisc_handler(unsigned long data)
/* port is good, unblock rport without going through erp */
zfcp_scsi_schedule_rport_register(port);
out:
+ atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
zfcp_port_put(port);
kfree(adisc);
}
@@ -450,28 +462,36 @@ void zfcp_fc_link_test_work(struct work_struct *work)
port->rport_task = RPORT_DEL;
zfcp_scsi_rport_work(&port->rport_work);
+ /* only issue one test command at one time per port */
+ if (atomic_read(&port->status) & ZFCP_STATUS_PORT_LINK_TEST)
+ goto out;
+
+ atomic_set_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
+
retval = zfcp_fc_adisc(port);
if (retval == 0)
return;
/* send of ADISC was not possible */
+ atomic_clear_mask(ZFCP_STATUS_PORT_LINK_TEST, &port->status);
zfcp_erp_port_forced_reopen(port, 0, "fcltwk1", NULL);
+out:
zfcp_port_put(port);
}
/**
- * zfcp_test_link - lightweight link test procedure
+ * zfcp_fc_test_link - lightweight link test procedure
* @port: port to be tested
*
* Test status of a link to a remote port using the ELS command ADISC.
* If there is a problem with the remote port, error recovery steps
* will be triggered.
*/
-void zfcp_test_link(struct zfcp_port *port)
+void zfcp_fc_test_link(struct zfcp_port *port)
{
zfcp_port_get(port);
- if (!queue_work(zfcp_data.work_queue, &port->test_link_work))
+ if (!queue_work(port->adapter->work_queue, &port->test_link_work))
zfcp_port_put(port);
}
@@ -479,7 +499,7 @@ static void zfcp_free_sg_env(struct zfcp_gpn_ft *gpn_ft, int buf_num)
{
struct scatterlist *sg = &gpn_ft->sg_req;
- kfree(sg_virt(sg)); /* free request buffer */
+ kmem_cache_free(zfcp_data.gpn_ft_cache, sg_virt(sg));
zfcp_sg_free_table(gpn_ft->sg_resp, buf_num);
kfree(gpn_ft);
@@ -494,7 +514,7 @@ static struct zfcp_gpn_ft *zfcp_alloc_sg_env(int buf_num)
if (!gpn_ft)
return NULL;
- req = kzalloc(sizeof(struct ct_iu_gpn_ft_req), GFP_KERNEL);
+ req = kmem_cache_alloc(zfcp_data.gpn_ft_cache, GFP_KERNEL);
if (!req) {
kfree(gpn_ft);
gpn_ft = NULL;
@@ -511,9 +531,8 @@ out:
}
-static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
- struct zfcp_adapter *adapter,
- int max_bytes)
+static int zfcp_fc_send_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
+ struct zfcp_adapter *adapter, int max_bytes)
{
struct zfcp_send_ct *ct = &gpn_ft->ct;
struct ct_iu_gpn_ft_req *req = sg_virt(&gpn_ft->sg_req);
@@ -536,19 +555,18 @@ static int zfcp_scan_issue_gpn_ft(struct zfcp_gpn_ft *gpn_ft,
ct->wka_port = &adapter->gs->ds;
ct->handler = zfcp_fc_ns_handler;
ct->handler_data = (unsigned long)&compl_rec;
- ct->timeout = 10;
ct->req = &gpn_ft->sg_req;
ct->resp = gpn_ft->sg_resp;
init_completion(&compl_rec.done);
compl_rec.handler = NULL;
- ret = zfcp_fsf_send_ct(ct, NULL, NULL);
+ ret = zfcp_fsf_send_ct(ct, NULL);
if (!ret)
wait_for_completion(&compl_rec.done);
return ret;
}
-static void zfcp_validate_port(struct zfcp_port *port)
+static void zfcp_fc_validate_port(struct zfcp_port *port)
{
struct zfcp_adapter *adapter = port->adapter;
@@ -568,7 +586,7 @@ static void zfcp_validate_port(struct zfcp_port *port)
zfcp_port_dequeue(port);
}
-static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries)
+static int zfcp_fc_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries)
{
struct zfcp_send_ct *ct = &gpn_ft->ct;
struct scatterlist *sg = gpn_ft->sg_resp;
@@ -595,7 +613,7 @@ static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries)
return -E2BIG;
}
- down(&zfcp_data.config_sema);
+ mutex_lock(&zfcp_data.config_mutex);
/* first entry is the header */
for (x = 1; x < max_entries && !last; x++) {
@@ -628,16 +646,16 @@ static int zfcp_scan_eval_gpn_ft(struct zfcp_gpn_ft *gpn_ft, int max_entries)
zfcp_erp_wait(adapter);
list_for_each_entry_safe(port, tmp, &adapter->port_list_head, list)
- zfcp_validate_port(port);
- up(&zfcp_data.config_sema);
+ zfcp_fc_validate_port(port);
+ mutex_unlock(&zfcp_data.config_mutex);
return ret;
}
/**
- * zfcp_scan_ports - scan remote ports and attach new ports
+ * zfcp_fc_scan_ports - scan remote ports and attach new ports
* @adapter: pointer to struct zfcp_adapter
*/
-int zfcp_scan_ports(struct zfcp_adapter *adapter)
+int zfcp_fc_scan_ports(struct zfcp_adapter *adapter)
{
int ret, i;
struct zfcp_gpn_ft *gpn_ft;
@@ -652,7 +670,7 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter)
fc_host_port_type(adapter->scsi_host) != FC_PORTTYPE_NPIV)
return 0;
- ret = zfcp_wka_port_get(&adapter->gs->ds);
+ ret = zfcp_fc_wka_port_get(&adapter->gs->ds);
if (ret)
return ret;
@@ -663,9 +681,9 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter)
}
for (i = 0; i < 3; i++) {
- ret = zfcp_scan_issue_gpn_ft(gpn_ft, adapter, max_bytes);
+ ret = zfcp_fc_send_gpn_ft(gpn_ft, adapter, max_bytes);
if (!ret) {
- ret = zfcp_scan_eval_gpn_ft(gpn_ft, max_entries);
+ ret = zfcp_fc_eval_gpn_ft(gpn_ft, max_entries);
if (ret == -EAGAIN)
ssleep(1);
else
@@ -674,14 +692,14 @@ int zfcp_scan_ports(struct zfcp_adapter *adapter)
}
zfcp_free_sg_env(gpn_ft, buf_num);
out:
- zfcp_wka_port_put(&adapter->gs->ds);
+ zfcp_fc_wka_port_put(&adapter->gs->ds);
return ret;
}
-void _zfcp_scan_ports_later(struct work_struct *work)
+void _zfcp_fc_scan_ports_later(struct work_struct *work)
{
- zfcp_scan_ports(container_of(work, struct zfcp_adapter, scan_work));
+ zfcp_fc_scan_ports(container_of(work, struct zfcp_adapter, scan_work));
}
struct zfcp_els_fc_job {
@@ -732,7 +750,7 @@ int zfcp_fc_execute_els_fc_job(struct fc_bsg_job *job)
els_fc_job->els.adapter = adapter;
if (rport) {
read_lock_irq(&zfcp_data.config_lock);
- port = rport->dd_data;
+ port = zfcp_get_port_by_wwpn(adapter, rport->port_name);
if (port)
els_fc_job->els.d_id = port->d_id;
read_unlock_irq(&zfcp_data.config_lock);
@@ -771,7 +789,7 @@ static void zfcp_fc_generic_ct_handler(unsigned long data)
job->state_flags = FC_RQST_STATE_DONE;
job->job_done(job);
- zfcp_wka_port_put(ct_fc_job->ct.wka_port);
+ zfcp_fc_wka_port_put(ct_fc_job->ct.wka_port);
kfree(ct_fc_job);
}
@@ -817,7 +835,7 @@ int zfcp_fc_execute_ct_fc_job(struct fc_bsg_job *job)
return -EINVAL; /* no such service */
}
- ret = zfcp_wka_port_get(ct_fc_job->ct.wka_port);
+ ret = zfcp_fc_wka_port_get(ct_fc_job->ct.wka_port);
if (ret) {
kfree(ct_fc_job);
return ret;
@@ -825,16 +843,40 @@ int zfcp_fc_execute_ct_fc_job(struct fc_bsg_job *job)
ct_fc_job->ct.req = job->request_payload.sg_list;
ct_fc_job->ct.resp = job->reply_payload.sg_list;
- ct_fc_job->ct.timeout = ZFCP_FSF_REQUEST_TIMEOUT;
ct_fc_job->ct.handler = zfcp_fc_generic_ct_handler;
ct_fc_job->ct.handler_data = (unsigned long) ct_fc_job;
ct_fc_job->ct.completion = NULL;
ct_fc_job->job = job;
- ret = zfcp_fsf_send_ct(&ct_fc_job->ct, NULL, NULL);
+ ret = zfcp_fsf_send_ct(&ct_fc_job->ct, NULL);
if (ret) {
kfree(ct_fc_job);
- zfcp_wka_port_put(ct_fc_job->ct.wka_port);
+ zfcp_fc_wka_port_put(ct_fc_job->ct.wka_port);
}
return ret;
}
+
+int zfcp_fc_gs_setup(struct zfcp_adapter *adapter)
+{
+ struct zfcp_wka_ports *wka_ports;
+
+ wka_ports = kzalloc(sizeof(struct zfcp_wka_ports), GFP_KERNEL);
+ if (!wka_ports)
+ return -ENOMEM;
+
+ adapter->gs = wka_ports;
+ zfcp_fc_wka_port_init(&wka_ports->ms, FC_FID_MGMT_SERV, adapter);
+ zfcp_fc_wka_port_init(&wka_ports->ts, FC_FID_TIME_SERV, adapter);
+ zfcp_fc_wka_port_init(&wka_ports->ds, FC_FID_DIR_SERV, adapter);
+ zfcp_fc_wka_port_init(&wka_ports->as, FC_FID_ALIASES, adapter);
+ zfcp_fc_wka_port_init(&wka_ports->ks, FC_FID_SEC_KEY, adapter);
+
+ return 0;
+}
+
+void zfcp_fc_gs_destroy(struct zfcp_adapter *adapter)
+{
+ kfree(adapter->gs);
+ adapter->gs = NULL;
+}
+
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index 47795fbf081..f09c863dc6b 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -11,9 +11,7 @@
#include <linux/blktrace_api.h>
#include "zfcp_ext.h"
-
-#define ZFCP_REQ_AUTO_CLEANUP 0x00000002
-#define ZFCP_REQ_NO_QTCB 0x00000008
+#include "zfcp_dbf.h"
static void zfcp_fsf_request_timeout_handler(unsigned long data)
{
@@ -111,43 +109,15 @@ static void zfcp_fsf_class_not_supp(struct zfcp_fsf_req *req)
void zfcp_fsf_req_free(struct zfcp_fsf_req *req)
{
if (likely(req->pool)) {
+ if (likely(req->qtcb))
+ mempool_free(req->qtcb, req->adapter->pool.qtcb_pool);
mempool_free(req, req->pool);
return;
}
- if (req->qtcb) {
- kmem_cache_free(zfcp_data.fsf_req_qtcb_cache, req);
- return;
- }
-}
-
-/**
- * zfcp_fsf_req_dismiss_all - dismiss all fsf requests
- * @adapter: pointer to struct zfcp_adapter
- *
- * Never ever call this without shutting down the adapter first.
- * Otherwise the adapter would continue using and corrupting s390 storage.
- * Included BUG_ON() call to ensure this is done.
- * ERP is supposed to be the only user of this function.
- */
-void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter)
-{
- struct zfcp_fsf_req *req, *tmp;
- unsigned long flags;
- LIST_HEAD(remove_queue);
- unsigned int i;
-
- BUG_ON(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP);
- spin_lock_irqsave(&adapter->req_list_lock, flags);
- for (i = 0; i < REQUEST_LIST_SIZE; i++)
- list_splice_init(&adapter->req_list[i], &remove_queue);
- spin_unlock_irqrestore(&adapter->req_list_lock, flags);
-
- list_for_each_entry_safe(req, tmp, &remove_queue, list) {
- list_del(&req->list);
- req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
- zfcp_fsf_req_complete(req);
- }
+ if (likely(req->qtcb))
+ kmem_cache_free(zfcp_data.qtcb_cache, req->qtcb);
+ kfree(req);
}
static void zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *req)
@@ -278,13 +248,13 @@ static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req)
struct fsf_status_read_buffer *sr_buf = req->data;
if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
- zfcp_hba_dbf_event_fsf_unsol("dism", adapter, sr_buf);
- mempool_free(sr_buf, adapter->pool.data_status_read);
+ zfcp_dbf_hba_fsf_unsol("dism", adapter->dbf, sr_buf);
+ mempool_free(sr_buf, adapter->pool.status_read_data);
zfcp_fsf_req_free(req);
return;
}
- zfcp_hba_dbf_event_fsf_unsol("read", adapter, sr_buf);
+ zfcp_dbf_hba_fsf_unsol("read", adapter->dbf, sr_buf);
switch (sr_buf->status_type) {
case FSF_STATUS_READ_PORT_CLOSED:
@@ -299,7 +269,7 @@ static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req)
dev_warn(&adapter->ccw_device->dev,
"The error threshold for checksum statistics "
"has been exceeded\n");
- zfcp_hba_dbf_event_berr(adapter, req);
+ zfcp_dbf_hba_berr(adapter->dbf, req);
break;
case FSF_STATUS_READ_LINK_DOWN:
zfcp_fsf_status_read_link_down(req);
@@ -331,11 +301,11 @@ static void zfcp_fsf_status_read_handler(struct zfcp_fsf_req *req)
break;
}
- mempool_free(sr_buf, adapter->pool.data_status_read);
+ mempool_free(sr_buf, adapter->pool.status_read_data);
zfcp_fsf_req_free(req);
atomic_inc(&adapter->stat_miss);
- queue_work(zfcp_data.work_queue, &adapter->stat_work);
+ queue_work(adapter->work_queue, &adapter->stat_work);
}
static void zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *req)
@@ -385,7 +355,7 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req)
struct fsf_qtcb *qtcb = req->qtcb;
union fsf_prot_status_qual *psq = &qtcb->prefix.prot_status_qual;
- zfcp_hba_dbf_event_fsf_response(req);
+ zfcp_dbf_hba_fsf_response(req);
if (req->status & ZFCP_STATUS_FSFREQ_DISMISSED) {
req->status |= ZFCP_STATUS_FSFREQ_ERROR |
@@ -458,7 +428,7 @@ static void zfcp_fsf_protstatus_eval(struct zfcp_fsf_req *req)
* is called to process the completion status and trigger further
* events related to the FSF request.
*/
-void zfcp_fsf_req_complete(struct zfcp_fsf_req *req)
+static void zfcp_fsf_req_complete(struct zfcp_fsf_req *req)
{
if (unlikely(req->fsf_command == FSF_QTCB_UNSOLICITED_STATUS)) {
zfcp_fsf_status_read_handler(req);
@@ -472,23 +442,40 @@ void zfcp_fsf_req_complete(struct zfcp_fsf_req *req)
if (req->erp_action)
zfcp_erp_notify(req->erp_action, 0);
- req->status |= ZFCP_STATUS_FSFREQ_COMPLETED;
if (likely(req->status & ZFCP_STATUS_FSFREQ_CLEANUP))
zfcp_fsf_req_free(req);
else
- /* notify initiator waiting for the requests completion */
- /*
- * FIXME: Race! We must not access fsf_req here as it might have been
- * cleaned up already due to the set ZFCP_STATUS_FSFREQ_COMPLETED
- * flag. It's an improbable case. But, we have the same paranoia for
- * the cleanup flag already.
- * Might better be handled using complete()?
- * (setting the flag and doing wakeup ought to be atomic
- * with regard to checking the flag as long as waitqueue is
- * part of the to be released structure)
- */
- wake_up(&req->completion_wq);
+ complete(&req->completion);
+}
+
+/**
+ * zfcp_fsf_req_dismiss_all - dismiss all fsf requests
+ * @adapter: pointer to struct zfcp_adapter
+ *
+ * Never ever call this without shutting down the adapter first.
+ * Otherwise the adapter would continue using and corrupting s390 storage.
+ * Included BUG_ON() call to ensure this is done.
+ * ERP is supposed to be the only user of this function.
+ */
+void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *adapter)
+{
+ struct zfcp_fsf_req *req, *tmp;
+ unsigned long flags;
+ LIST_HEAD(remove_queue);
+ unsigned int i;
+
+ BUG_ON(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP);
+ spin_lock_irqsave(&adapter->req_list_lock, flags);
+ for (i = 0; i < REQUEST_LIST_SIZE; i++)
+ list_splice_init(&adapter->req_list[i], &remove_queue);
+ spin_unlock_irqrestore(&adapter->req_list_lock, flags);
+
+ list_for_each_entry_safe(req, tmp, &remove_queue, list) {
+ list_del(&req->list);
+ req->status |= ZFCP_STATUS_FSFREQ_DISMISSED;
+ zfcp_fsf_req_complete(req);
+ }
}
static int zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *req)
@@ -650,79 +637,77 @@ static void zfcp_fsf_exchange_port_data_handler(struct zfcp_fsf_req *req)
}
}
-static int zfcp_fsf_sbal_check(struct zfcp_adapter *adapter)
+static int zfcp_fsf_sbal_check(struct zfcp_qdio *qdio)
{
- struct zfcp_qdio_queue *req_q = &adapter->req_q;
+ struct zfcp_qdio_queue *req_q = &qdio->req_q;
- spin_lock_bh(&adapter->req_q_lock);
+ spin_lock_bh(&qdio->req_q_lock);
if (atomic_read(&req_q->count))
return 1;
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return 0;
}
-static int zfcp_fsf_req_sbal_get(struct zfcp_adapter *adapter)
+static int zfcp_fsf_req_sbal_get(struct zfcp_qdio *qdio)
{
+ struct zfcp_adapter *adapter = qdio->adapter;
long ret;
- spin_unlock_bh(&adapter->req_q_lock);
- ret = wait_event_interruptible_timeout(adapter->request_wq,
- zfcp_fsf_sbal_check(adapter), 5 * HZ);
+ spin_unlock_bh(&qdio->req_q_lock);
+ ret = wait_event_interruptible_timeout(qdio->req_q_wq,
+ zfcp_fsf_sbal_check(qdio), 5 * HZ);
if (ret > 0)
return 0;
if (!ret) {
- atomic_inc(&adapter->qdio_outb_full);
+ atomic_inc(&qdio->req_q_full);
/* assume hanging outbound queue, try queue recovery */
zfcp_erp_adapter_reopen(adapter, 0, "fsrsg_1", NULL);
}
- spin_lock_bh(&adapter->req_q_lock);
+ spin_lock_bh(&qdio->req_q_lock);
return -EIO;
}
-static struct zfcp_fsf_req *zfcp_fsf_alloc_noqtcb(mempool_t *pool)
+static struct zfcp_fsf_req *zfcp_fsf_alloc(mempool_t *pool)
{
struct zfcp_fsf_req *req;
- req = mempool_alloc(pool, GFP_ATOMIC);
- if (!req)
+
+ if (likely(pool))
+ req = mempool_alloc(pool, GFP_ATOMIC);
+ else
+ req = kmalloc(sizeof(*req), GFP_ATOMIC);
+
+ if (unlikely(!req))
return NULL;
+
memset(req, 0, sizeof(*req));
req->pool = pool;
return req;
}
-static struct zfcp_fsf_req *zfcp_fsf_alloc_qtcb(mempool_t *pool)
+static struct fsf_qtcb *zfcp_qtcb_alloc(mempool_t *pool)
{
- struct zfcp_fsf_req_qtcb *qtcb;
+ struct fsf_qtcb *qtcb;
if (likely(pool))
qtcb = mempool_alloc(pool, GFP_ATOMIC);
else
- qtcb = kmem_cache_alloc(zfcp_data.fsf_req_qtcb_cache,
- GFP_ATOMIC);
+ qtcb = kmem_cache_alloc(zfcp_data.qtcb_cache, GFP_ATOMIC);
+
if (unlikely(!qtcb))
return NULL;
memset(qtcb, 0, sizeof(*qtcb));
- qtcb->fsf_req.qtcb = &qtcb->qtcb;
- qtcb->fsf_req.pool = pool;
-
- return &qtcb->fsf_req;
+ return qtcb;
}
-static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_adapter *adapter,
- u32 fsf_cmd, int req_flags,
- mempool_t *pool)
+static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_qdio *qdio,
+ u32 fsf_cmd, mempool_t *pool)
{
struct qdio_buffer_element *sbale;
-
- struct zfcp_fsf_req *req;
- struct zfcp_qdio_queue *req_q = &adapter->req_q;
-
- if (req_flags & ZFCP_REQ_NO_QTCB)
- req = zfcp_fsf_alloc_noqtcb(pool);
- else
- req = zfcp_fsf_alloc_qtcb(pool);
+ struct zfcp_qdio_queue *req_q = &qdio->req_q;
+ struct zfcp_adapter *adapter = qdio->adapter;
+ struct zfcp_fsf_req *req = zfcp_fsf_alloc(pool);
if (unlikely(!req))
return ERR_PTR(-ENOMEM);
@@ -732,22 +717,32 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_adapter *adapter,
INIT_LIST_HEAD(&req->list);
init_timer(&req->timer);
- init_waitqueue_head(&req->completion_wq);
+ init_completion(&req->completion);
req->adapter = adapter;
req->fsf_command = fsf_cmd;
req->req_id = adapter->req_no;
- req->sbal_number = 1;
- req->sbal_first = req_q->first;
- req->sbal_last = req_q->first;
- req->sbale_curr = 1;
+ req->queue_req.sbal_number = 1;
+ req->queue_req.sbal_first = req_q->first;
+ req->queue_req.sbal_last = req_q->first;
+ req->queue_req.sbale_curr = 1;
- sbale = zfcp_qdio_sbale_req(req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].addr = (void *) req->req_id;
sbale[0].flags |= SBAL_FLAGS0_COMMAND;
- if (likely(req->qtcb)) {
- req->qtcb->prefix.req_seq_no = req->adapter->fsf_req_seq_no;
+ if (likely(fsf_cmd != FSF_QTCB_UNSOLICITED_STATUS)) {
+ if (likely(pool))
+ req->qtcb = zfcp_qtcb_alloc(adapter->pool.qtcb_pool);
+ else
+ req->qtcb = zfcp_qtcb_alloc(NULL);
+
+ if (unlikely(!req->qtcb)) {
+ zfcp_fsf_req_free(req);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ req->qtcb->prefix.req_seq_no = adapter->fsf_req_seq_no;
req->qtcb->prefix.req_id = req->req_id;
req->qtcb->prefix.ulp_info = 26;
req->qtcb->prefix.qtcb_type = fsf_qtcb_type[req->fsf_command];
@@ -765,15 +760,13 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_adapter *adapter,
return ERR_PTR(-EIO);
}
- if (likely(req_flags & ZFCP_REQ_AUTO_CLEANUP))
- req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
-
return req;
}
static int zfcp_fsf_req_send(struct zfcp_fsf_req *req)
{
struct zfcp_adapter *adapter = req->adapter;
+ struct zfcp_qdio *qdio = adapter->qdio;
unsigned long flags;
int idx;
int with_qtcb = (req->qtcb != NULL);
@@ -784,9 +777,9 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *req)
list_add_tail(&req->list, &adapter->req_list[idx]);
spin_unlock_irqrestore(&adapter->req_list_lock, flags);
- req->qdio_outb_usage = atomic_read(&adapter->req_q.count);
+ req->queue_req.qdio_outb_usage = atomic_read(&qdio->req_q.count);
req->issued = get_clock();
- if (zfcp_qdio_send(req)) {
+ if (zfcp_qdio_send(qdio, &req->queue_req)) {
del_timer(&req->timer);
spin_lock_irqsave(&adapter->req_list_lock, flags);
/* lookup request again, list might have changed */
@@ -811,38 +804,37 @@ static int zfcp_fsf_req_send(struct zfcp_fsf_req *req)
* @req_flags: request flags
* Returns: 0 on success, ERROR otherwise
*/
-int zfcp_fsf_status_read(struct zfcp_adapter *adapter)
+int zfcp_fsf_status_read(struct zfcp_qdio *qdio)
{
+ struct zfcp_adapter *adapter = qdio->adapter;
struct zfcp_fsf_req *req;
struct fsf_status_read_buffer *sr_buf;
struct qdio_buffer_element *sbale;
int retval = -EIO;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_UNSOLICITED_STATUS,
- ZFCP_REQ_NO_QTCB,
- adapter->pool.fsf_req_status_read);
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_UNSOLICITED_STATUS,
+ adapter->pool.status_read_req);
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(req);
- sbale[0].flags |= SBAL_FLAGS0_TYPE_STATUS;
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY;
- req->sbale_curr = 2;
+ req->queue_req.sbale_curr = 2;
- sr_buf = mempool_alloc(adapter->pool.data_status_read, GFP_ATOMIC);
+ sr_buf = mempool_alloc(adapter->pool.status_read_data, GFP_ATOMIC);
if (!sr_buf) {
retval = -ENOMEM;
goto failed_buf;
}
memset(sr_buf, 0, sizeof(*sr_buf));
req->data = sr_buf;
- sbale = zfcp_qdio_sbale_curr(req);
+ sbale = zfcp_qdio_sbale_curr(qdio, &req->queue_req);
sbale->addr = (void *) sr_buf;
sbale->length = sizeof(*sr_buf);
@@ -853,12 +845,12 @@ int zfcp_fsf_status_read(struct zfcp_adapter *adapter)
goto out;
failed_req_send:
- mempool_free(sr_buf, adapter->pool.data_status_read);
+ mempool_free(sr_buf, adapter->pool.status_read_data);
failed_buf:
zfcp_fsf_req_free(req);
- zfcp_hba_dbf_event_fsf_unsol("fail", adapter, NULL);
+ zfcp_dbf_hba_fsf_unsol("fail", adapter->dbf, NULL);
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return retval;
}
@@ -900,7 +892,7 @@ static void zfcp_fsf_abort_fcp_command_handler(struct zfcp_fsf_req *req)
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (fsq->word[0]) {
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
- zfcp_test_link(unit->port);
+ zfcp_fc_test_link(unit->port);
/* fall through */
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
@@ -925,13 +917,13 @@ struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
{
struct qdio_buffer_element *sbale;
struct zfcp_fsf_req *req = NULL;
- struct zfcp_adapter *adapter = unit->port->adapter;
+ struct zfcp_qdio *qdio = unit->port->adapter->qdio;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_ABORT_FCP_CMND,
- 0, adapter->pool.fsf_req_abort);
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_ABORT_FCP_CMND,
+ qdio->adapter->pool.scsi_abort);
if (IS_ERR(req)) {
req = NULL;
goto out;
@@ -941,7 +933,7 @@ struct zfcp_fsf_req *zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
ZFCP_STATUS_COMMON_UNBLOCKED)))
goto out_error_free;
- sbale = zfcp_qdio_sbale_req(req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -959,7 +951,7 @@ out_error_free:
zfcp_fsf_req_free(req);
req = NULL;
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return req;
}
@@ -976,7 +968,7 @@ static void zfcp_fsf_send_ct_handler(struct zfcp_fsf_req *req)
switch (header->fsf_status) {
case FSF_GOOD:
- zfcp_san_dbf_event_ct_response(req);
+ zfcp_dbf_san_ct_response(req);
send_ct->status = 0;
break;
case FSF_SERVICE_CLASS_NOT_SUPPORTED:
@@ -1035,8 +1027,10 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
struct scatterlist *sg_resp,
int max_sbals)
{
- struct qdio_buffer_element *sbale = zfcp_qdio_sbale_req(req);
- u32 feat = req->adapter->adapter_features;
+ struct zfcp_adapter *adapter = req->adapter;
+ struct qdio_buffer_element *sbale = zfcp_qdio_sbale_req(adapter->qdio,
+ &req->queue_req);
+ u32 feat = adapter->adapter_features;
int bytes;
if (!(feat & FSF_FEATURE_ELS_CT_CHAINED_SBALS)) {
@@ -1053,18 +1047,25 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
return 0;
}
- bytes = zfcp_qdio_sbals_from_sg(req, SBAL_FLAGS0_TYPE_WRITE_READ,
+ bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->queue_req,
+ SBAL_FLAGS0_TYPE_WRITE_READ,
sg_req, max_sbals);
if (bytes <= 0)
return -EIO;
req->qtcb->bottom.support.req_buf_length = bytes;
- req->sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
+ req->queue_req.sbale_curr = ZFCP_LAST_SBALE_PER_SBAL;
- bytes = zfcp_qdio_sbals_from_sg(req, SBAL_FLAGS0_TYPE_WRITE_READ,
+ bytes = zfcp_qdio_sbals_from_sg(adapter->qdio, &req->queue_req,
+ SBAL_FLAGS0_TYPE_WRITE_READ,
sg_resp, max_sbals);
if (bytes <= 0)
return -EIO;
+
+ /* common settings for ct/gs and els requests */
req->qtcb->bottom.support.resp_buf_length = bytes;
+ req->qtcb->bottom.support.service_class = FSF_CLASS_3;
+ req->qtcb->bottom.support.timeout = 2 * R_A_TOV;
+ zfcp_fsf_start_timer(req, 2 * R_A_TOV + 10);
return 0;
}
@@ -1073,27 +1074,26 @@ static int zfcp_fsf_setup_ct_els_sbals(struct zfcp_fsf_req *req,
* zfcp_fsf_send_ct - initiate a Generic Service request (FC-GS)
* @ct: pointer to struct zfcp_send_ct with data for request
* @pool: if non-null this mempool is used to allocate struct zfcp_fsf_req
- * @erp_action: if non-null the Generic Service request sent within ERP
*/
-int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
- struct zfcp_erp_action *erp_action)
+int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool)
{
struct zfcp_wka_port *wka_port = ct->wka_port;
- struct zfcp_adapter *adapter = wka_port->adapter;
+ struct zfcp_qdio *qdio = wka_port->adapter->qdio;
struct zfcp_fsf_req *req;
int ret = -EIO;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_GENERIC,
- ZFCP_REQ_AUTO_CLEANUP, pool);
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_SEND_GENERIC, pool);
+
if (IS_ERR(req)) {
ret = PTR_ERR(req);
goto out;
}
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
ret = zfcp_fsf_setup_ct_els_sbals(req, ct->req, ct->resp,
FSF_MAX_SBALS_PER_REQ);
if (ret)
@@ -1101,18 +1101,9 @@ int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
req->handler = zfcp_fsf_send_ct_handler;
req->qtcb->header.port_handle = wka_port->handle;
- req->qtcb->bottom.support.service_class = FSF_CLASS_3;
- req->qtcb->bottom.support.timeout = ct->timeout;
req->data = ct;
- zfcp_san_dbf_event_ct_request(req);
-
- if (erp_action) {
- erp_action->fsf_req = req;
- req->erp_action = erp_action;
- zfcp_fsf_start_erp_timer(req);
- } else
- zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
+ zfcp_dbf_san_ct_request(req);
ret = zfcp_fsf_req_send(req);
if (ret)
@@ -1122,10 +1113,8 @@ int zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
failed_send:
zfcp_fsf_req_free(req);
- if (erp_action)
- erp_action->fsf_req = NULL;
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return ret;
}
@@ -1142,7 +1131,7 @@ static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req)
switch (header->fsf_status) {
case FSF_GOOD:
- zfcp_san_dbf_event_els_response(req);
+ zfcp_dbf_san_els_response(req);
send_els->status = 0;
break;
case FSF_SERVICE_CLASS_NOT_SUPPORTED:
@@ -1152,7 +1141,7 @@ static void zfcp_fsf_send_els_handler(struct zfcp_fsf_req *req)
switch (header->fsf_status_qual.word[0]){
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
if (port && (send_els->ls_code != ZFCP_LS_ADISC))
- zfcp_test_link(port);
+ zfcp_fc_test_link(port);
/*fall through */
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
case FSF_SQ_RETRY_IF_POSSIBLE:
@@ -1188,35 +1177,32 @@ skip_fsfstatus:
int zfcp_fsf_send_els(struct zfcp_send_els *els)
{
struct zfcp_fsf_req *req;
- struct zfcp_adapter *adapter = els->adapter;
- struct fsf_qtcb_bottom_support *bottom;
+ struct zfcp_qdio *qdio = els->adapter->qdio;
int ret = -EIO;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_SEND_ELS,
- ZFCP_REQ_AUTO_CLEANUP, NULL);
+
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_SEND_ELS, NULL);
+
if (IS_ERR(req)) {
ret = PTR_ERR(req);
goto out;
}
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
ret = zfcp_fsf_setup_ct_els_sbals(req, els->req, els->resp, 2);
if (ret)
goto failed_send;
- bottom = &req->qtcb->bottom.support;
+ req->qtcb->bottom.support.d_id = els->d_id;
req->handler = zfcp_fsf_send_els_handler;
- bottom->d_id = els->d_id;
- bottom->service_class = FSF_CLASS_3;
- bottom->timeout = 2 * R_A_TOV;
req->data = els;
- zfcp_san_dbf_event_els_request(req);
+ zfcp_dbf_san_els_request(req);
- zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
ret = zfcp_fsf_req_send(req);
if (ret)
goto failed_send;
@@ -1226,7 +1212,7 @@ int zfcp_fsf_send_els(struct zfcp_send_els *els)
failed_send:
zfcp_fsf_req_free(req);
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return ret;
}
@@ -1234,22 +1220,23 @@ int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
{
struct qdio_buffer_element *sbale;
struct zfcp_fsf_req *req;
- struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_qdio *qdio = erp_action->adapter->qdio;
int retval = -EIO;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter,
- FSF_QTCB_EXCHANGE_CONFIG_DATA,
- ZFCP_REQ_AUTO_CLEANUP,
- adapter->pool.fsf_req_erp);
+
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_CONFIG_DATA,
+ qdio->adapter->pool.erp_req);
+
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(req);
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1269,29 +1256,29 @@ int zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
erp_action->fsf_req = NULL;
}
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return retval;
}
-int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter,
+int zfcp_fsf_exchange_config_data_sync(struct zfcp_qdio *qdio,
struct fsf_qtcb_bottom_config *data)
{
struct qdio_buffer_element *sbale;
struct zfcp_fsf_req *req = NULL;
int retval = -EIO;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out_unlock;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_CONFIG_DATA,
- 0, NULL);
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_CONFIG_DATA, NULL);
+
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out_unlock;
}
- sbale = zfcp_qdio_sbale_req(req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
req->handler = zfcp_fsf_exchange_config_data_handler;
@@ -1307,16 +1294,15 @@ int zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter,
zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
retval = zfcp_fsf_req_send(req);
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
if (!retval)
- wait_event(req->completion_wq,
- req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ wait_for_completion(&req->completion);
zfcp_fsf_req_free(req);
return retval;
out_unlock:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return retval;
}
@@ -1327,26 +1313,28 @@ out_unlock:
*/
int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action)
{
+ struct zfcp_qdio *qdio = erp_action->adapter->qdio;
struct qdio_buffer_element *sbale;
struct zfcp_fsf_req *req;
- struct zfcp_adapter *adapter = erp_action->adapter;
int retval = -EIO;
- if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT))
+ if (!(qdio->adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT))
return -EOPNOTSUPP;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA,
- ZFCP_REQ_AUTO_CLEANUP,
- adapter->pool.fsf_req_erp);
+
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_PORT_DATA,
+ qdio->adapter->pool.erp_req);
+
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(req);
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1361,32 +1349,32 @@ int zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action)
erp_action->fsf_req = NULL;
}
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return retval;
}
/**
* zfcp_fsf_exchange_port_data_sync - request information about local port
- * @adapter: pointer to struct zfcp_adapter
+ * @qdio: pointer to struct zfcp_qdio
* @data: pointer to struct fsf_qtcb_bottom_port
* Returns: 0 on success, error otherwise
*/
-int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter,
+int zfcp_fsf_exchange_port_data_sync(struct zfcp_qdio *qdio,
struct fsf_qtcb_bottom_port *data)
{
struct qdio_buffer_element *sbale;
struct zfcp_fsf_req *req = NULL;
int retval = -EIO;
- if (!(adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT))
+ if (!(qdio->adapter->adapter_features & FSF_FEATURE_HBAAPI_MANAGEMENT))
return -EOPNOTSUPP;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out_unlock;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_EXCHANGE_PORT_DATA, 0,
- NULL);
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_EXCHANGE_PORT_DATA, NULL);
+
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out_unlock;
@@ -1395,24 +1383,24 @@ int zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter,
if (data)
req->data = data;
- sbale = zfcp_qdio_sbale_req(req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
req->handler = zfcp_fsf_exchange_port_data_handler;
zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
retval = zfcp_fsf_req_send(req);
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
if (!retval)
- wait_event(req->completion_wq,
- req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ wait_for_completion(&req->completion);
+
zfcp_fsf_req_free(req);
return retval;
out_unlock:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return retval;
}
@@ -1498,25 +1486,25 @@ out:
int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
{
struct qdio_buffer_element *sbale;
- struct zfcp_adapter *adapter = erp_action->adapter;
- struct zfcp_fsf_req *req;
+ struct zfcp_qdio *qdio = erp_action->adapter->qdio;
struct zfcp_port *port = erp_action->port;
+ struct zfcp_fsf_req *req;
int retval = -EIO;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter,
- FSF_QTCB_OPEN_PORT_WITH_DID,
- ZFCP_REQ_AUTO_CLEANUP,
- adapter->pool.fsf_req_erp);
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_OPEN_PORT_WITH_DID,
+ qdio->adapter->pool.erp_req);
+
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(req);
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1535,7 +1523,7 @@ int zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
zfcp_port_put(port);
}
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return retval;
}
@@ -1569,23 +1557,24 @@ static void zfcp_fsf_close_port_handler(struct zfcp_fsf_req *req)
int zfcp_fsf_close_port(struct zfcp_erp_action *erp_action)
{
struct qdio_buffer_element *sbale;
- struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_qdio *qdio = erp_action->adapter->qdio;
struct zfcp_fsf_req *req;
int retval = -EIO;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_CLOSE_PORT,
- ZFCP_REQ_AUTO_CLEANUP,
- adapter->pool.fsf_req_erp);
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_CLOSE_PORT,
+ qdio->adapter->pool.erp_req);
+
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(req);
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1602,7 +1591,7 @@ int zfcp_fsf_close_port(struct zfcp_erp_action *erp_action)
erp_action->fsf_req = NULL;
}
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return retval;
}
@@ -1645,24 +1634,24 @@ out:
int zfcp_fsf_open_wka_port(struct zfcp_wka_port *wka_port)
{
struct qdio_buffer_element *sbale;
- struct zfcp_adapter *adapter = wka_port->adapter;
+ struct zfcp_qdio *qdio = wka_port->adapter->qdio;
struct zfcp_fsf_req *req;
int retval = -EIO;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter,
- FSF_QTCB_OPEN_PORT_WITH_DID,
- ZFCP_REQ_AUTO_CLEANUP,
- adapter->pool.fsf_req_erp);
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_OPEN_PORT_WITH_DID,
+ qdio->adapter->pool.erp_req);
+
if (unlikely(IS_ERR(req))) {
retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(req);
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1675,7 +1664,7 @@ int zfcp_fsf_open_wka_port(struct zfcp_wka_port *wka_port)
if (retval)
zfcp_fsf_req_free(req);
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return retval;
}
@@ -1700,23 +1689,24 @@ static void zfcp_fsf_close_wka_port_handler(struct zfcp_fsf_req *req)
int zfcp_fsf_close_wka_port(struct zfcp_wka_port *wka_port)
{
struct qdio_buffer_element *sbale;
- struct zfcp_adapter *adapter = wka_port->adapter;
+ struct zfcp_qdio *qdio = wka_port->adapter->qdio;
struct zfcp_fsf_req *req;
int retval = -EIO;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_CLOSE_PORT,
- ZFCP_REQ_AUTO_CLEANUP,
- adapter->pool.fsf_req_erp);
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_CLOSE_PORT,
+ qdio->adapter->pool.erp_req);
+
if (unlikely(IS_ERR(req))) {
retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(req);
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1729,7 +1719,7 @@ int zfcp_fsf_close_wka_port(struct zfcp_wka_port *wka_port)
if (retval)
zfcp_fsf_req_free(req);
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return retval;
}
@@ -1791,23 +1781,24 @@ static void zfcp_fsf_close_physical_port_handler(struct zfcp_fsf_req *req)
int zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
{
struct qdio_buffer_element *sbale;
- struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_qdio *qdio = erp_action->adapter->qdio;
struct zfcp_fsf_req *req;
int retval = -EIO;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_CLOSE_PHYSICAL_PORT,
- ZFCP_REQ_AUTO_CLEANUP,
- adapter->pool.fsf_req_erp);
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_CLOSE_PHYSICAL_PORT,
+ qdio->adapter->pool.erp_req);
+
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(req);
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1824,7 +1815,7 @@ int zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
erp_action->fsf_req = NULL;
}
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return retval;
}
@@ -1895,7 +1886,7 @@ static void zfcp_fsf_open_unit_handler(struct zfcp_fsf_req *req)
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (header->fsf_status_qual.word[0]) {
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
- zfcp_test_link(unit->port);
+ zfcp_fc_test_link(unit->port);
/* fall through */
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
@@ -1964,22 +1955,24 @@ int zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
{
struct qdio_buffer_element *sbale;
struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_qdio *qdio = adapter->qdio;
struct zfcp_fsf_req *req;
int retval = -EIO;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_OPEN_LUN,
- ZFCP_REQ_AUTO_CLEANUP,
- adapter->pool.fsf_req_erp);
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_OPEN_LUN,
+ adapter->pool.erp_req);
+
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(req);
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -2000,7 +1993,7 @@ int zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
erp_action->fsf_req = NULL;
}
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return retval;
}
@@ -2028,7 +2021,7 @@ static void zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *req)
case FSF_ADAPTER_STATUS_AVAILABLE:
switch (req->qtcb->header.fsf_status_qual.word[0]) {
case FSF_SQ_INVOKE_LINK_TEST_PROCEDURE:
- zfcp_test_link(unit->port);
+ zfcp_fc_test_link(unit->port);
/* fall through */
case FSF_SQ_ULP_DEPENDENT_ERP_REQUIRED:
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
@@ -2049,22 +2042,24 @@ static void zfcp_fsf_close_unit_handler(struct zfcp_fsf_req *req)
int zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action)
{
struct qdio_buffer_element *sbale;
- struct zfcp_adapter *adapter = erp_action->adapter;
+ struct zfcp_qdio *qdio = erp_action->adapter->qdio;
struct zfcp_fsf_req *req;
int retval = -EIO;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_CLOSE_LUN,
- ZFCP_REQ_AUTO_CLEANUP,
- adapter->pool.fsf_req_erp);
+
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_CLOSE_LUN,
+ qdio->adapter->pool.erp_req);
+
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out;
}
- sbale = zfcp_qdio_sbale_req(req);
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -2082,7 +2077,7 @@ int zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action)
erp_action->fsf_req = NULL;
}
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return retval;
}
@@ -2141,8 +2136,8 @@ static void zfcp_fsf_trace_latency(struct zfcp_fsf_req *fsf_req)
}
if (fsf_req->status & ZFCP_STATUS_FSFREQ_ERROR)
trace.flags |= ZFCP_BLK_REQ_ERROR;
- trace.inb_usage = fsf_req->qdio_inb_usage;
- trace.outb_usage = fsf_req->qdio_outb_usage;
+ trace.inb_usage = fsf_req->queue_req.qdio_inb_usage;
+ trace.outb_usage = fsf_req->queue_req.qdio_outb_usage;
blk_add_driver_data(req->q, req, &trace, sizeof(trace));
}
@@ -2215,11 +2210,11 @@ static void zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *req)
}
skip_fsfstatus:
if (scpnt->result != 0)
- zfcp_scsi_dbf_event_result("erro", 3, req->adapter, scpnt, req);
+ zfcp_dbf_scsi_result("erro", 3, req->adapter->dbf, scpnt, req);
else if (scpnt->retries > 0)
- zfcp_scsi_dbf_event_result("retr", 4, req->adapter, scpnt, req);
+ zfcp_dbf_scsi_result("retr", 4, req->adapter->dbf, scpnt, req);
else
- zfcp_scsi_dbf_event_result("norm", 6, req->adapter, scpnt, req);
+ zfcp_dbf_scsi_result("norm", 6, req->adapter->dbf, scpnt, req);
scpnt->host_scribble = NULL;
(scpnt->scsi_done) (scpnt);
@@ -2309,7 +2304,7 @@ static void zfcp_fsf_send_fcp_command_handler(struct zfcp_fsf_req *req)
case FSF_ADAPTER_STATUS_AVAILABLE:
if (header->fsf_status_qual.word[0] ==
FSF_SQ_INVOKE_LINK_TEST_PROCEDURE)
- zfcp_test_link(unit->port);
+ zfcp_fc_test_link(unit->port);
req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
}
@@ -2350,24 +2345,27 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
unsigned int sbtype = SBAL_FLAGS0_TYPE_READ;
int real_bytes, retval = -EIO;
struct zfcp_adapter *adapter = unit->port->adapter;
+ struct zfcp_qdio *qdio = adapter->qdio;
if (unlikely(!(atomic_read(&unit->status) &
ZFCP_STATUS_COMMON_UNBLOCKED)))
return -EBUSY;
- spin_lock(&adapter->req_q_lock);
- if (atomic_read(&adapter->req_q.count) <= 0) {
- atomic_inc(&adapter->qdio_outb_full);
+ spin_lock(&qdio->req_q_lock);
+ if (atomic_read(&qdio->req_q.count) <= 0) {
+ atomic_inc(&qdio->req_q_full);
goto out;
}
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND,
- ZFCP_REQ_AUTO_CLEANUP,
- adapter->pool.fsf_req_scsi);
+
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_FCP_CMND,
+ adapter->pool.scsi_req);
+
if (IS_ERR(req)) {
retval = PTR_ERR(req);
goto out;
}
+ req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
zfcp_unit_get(unit);
req->unit = unit;
req->data = scsi_cmnd;
@@ -2419,11 +2417,11 @@ int zfcp_fsf_send_fcp_command_task(struct zfcp_unit *unit,
req->qtcb->bottom.io.fcp_cmnd_length = sizeof(struct fcp_cmnd_iu) +
fcp_cmnd_iu->add_fcp_cdb_length + sizeof(u32);
- real_bytes = zfcp_qdio_sbals_from_sg(req, sbtype,
+ real_bytes = zfcp_qdio_sbals_from_sg(qdio, &req->queue_req, sbtype,
scsi_sglist(scsi_cmnd),
FSF_MAX_SBALS_PER_REQ);
if (unlikely(real_bytes < 0)) {
- if (req->sbal_number >= FSF_MAX_SBALS_PER_REQ) {
+ if (req->queue_req.sbal_number >= FSF_MAX_SBALS_PER_REQ) {
dev_err(&adapter->ccw_device->dev,
"Oversize data package, unit 0x%016Lx "
"on port 0x%016Lx closed\n",
@@ -2448,7 +2446,7 @@ failed_scsi_cmnd:
zfcp_fsf_req_free(req);
scsi_cmnd->host_scribble = NULL;
out:
- spin_unlock(&adapter->req_q_lock);
+ spin_unlock(&qdio->req_q_lock);
return retval;
}
@@ -2463,17 +2461,19 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags)
struct qdio_buffer_element *sbale;
struct zfcp_fsf_req *req = NULL;
struct fcp_cmnd_iu *fcp_cmnd_iu;
- struct zfcp_adapter *adapter = unit->port->adapter;
+ struct zfcp_qdio *qdio = unit->port->adapter->qdio;
if (unlikely(!(atomic_read(&unit->status) &
ZFCP_STATUS_COMMON_UNBLOCKED)))
return NULL;
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter, FSF_QTCB_FCP_CMND, 0,
- adapter->pool.fsf_req_scsi);
+
+ req = zfcp_fsf_req_create(qdio, FSF_QTCB_FCP_CMND,
+ qdio->adapter->pool.scsi_req);
+
if (IS_ERR(req)) {
req = NULL;
goto out;
@@ -2489,7 +2489,7 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags)
req->qtcb->bottom.io.fcp_cmnd_length = sizeof(struct fcp_cmnd_iu) +
sizeof(u32);
- sbale = zfcp_qdio_sbale_req(req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -2504,7 +2504,7 @@ struct zfcp_fsf_req *zfcp_fsf_send_fcp_ctm(struct zfcp_unit *unit, u8 tm_flags)
zfcp_fsf_req_free(req);
req = NULL;
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
return req;
}
@@ -2522,6 +2522,7 @@ struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter,
struct zfcp_fsf_cfdc *fsf_cfdc)
{
struct qdio_buffer_element *sbale;
+ struct zfcp_qdio *qdio = adapter->qdio;
struct zfcp_fsf_req *req = NULL;
struct fsf_qtcb_bottom_support *bottom;
int direction, retval = -EIO, bytes;
@@ -2540,11 +2541,11 @@ struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter,
return ERR_PTR(-EINVAL);
}
- spin_lock_bh(&adapter->req_q_lock);
- if (zfcp_fsf_req_sbal_get(adapter))
+ spin_lock_bh(&qdio->req_q_lock);
+ if (zfcp_fsf_req_sbal_get(qdio))
goto out;
- req = zfcp_fsf_req_create(adapter, fsf_cfdc->command, 0, NULL);
+ req = zfcp_fsf_req_create(qdio, fsf_cfdc->command, NULL);
if (IS_ERR(req)) {
retval = -EPERM;
goto out;
@@ -2552,14 +2553,15 @@ struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter,
req->handler = zfcp_fsf_control_file_handler;
- sbale = zfcp_qdio_sbale_req(req);
+ sbale = zfcp_qdio_sbale_req(qdio, &req->queue_req);
sbale[0].flags |= direction;
bottom = &req->qtcb->bottom.support;
bottom->operation_subtype = FSF_CFDC_OPERATION_SUBTYPE;
bottom->option = fsf_cfdc->option;
- bytes = zfcp_qdio_sbals_from_sg(req, direction, fsf_cfdc->sg,
+ bytes = zfcp_qdio_sbals_from_sg(qdio, &req->queue_req,
+ direction, fsf_cfdc->sg,
FSF_MAX_SBALS_PER_REQ);
if (bytes != ZFCP_CFDC_MAX_SIZE) {
zfcp_fsf_req_free(req);
@@ -2569,12 +2571,53 @@ struct zfcp_fsf_req *zfcp_fsf_control_file(struct zfcp_adapter *adapter,
zfcp_fsf_start_timer(req, ZFCP_FSF_REQUEST_TIMEOUT);
retval = zfcp_fsf_req_send(req);
out:
- spin_unlock_bh(&adapter->req_q_lock);
+ spin_unlock_bh(&qdio->req_q_lock);
if (!retval) {
- wait_event(req->completion_wq,
- req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ wait_for_completion(&req->completion);
return req;
}
return ERR_PTR(retval);
}
+
+/**
+ * zfcp_fsf_reqid_check - validate req_id contained in SBAL returned by QDIO
+ * @adapter: pointer to struct zfcp_adapter
+ * @sbal_idx: response queue index of SBAL to be processed
+ */
+void zfcp_fsf_reqid_check(struct zfcp_qdio *qdio, int sbal_idx)
+{
+ struct zfcp_adapter *adapter = qdio->adapter;
+ struct qdio_buffer *sbal = qdio->resp_q.sbal[sbal_idx];
+ struct qdio_buffer_element *sbale;
+ struct zfcp_fsf_req *fsf_req;
+ unsigned long flags, req_id;
+ int idx;
+
+ for (idx = 0; idx < QDIO_MAX_ELEMENTS_PER_BUFFER; idx++) {
+
+ sbale = &sbal->element[idx];
+ req_id = (unsigned long) sbale->addr;
+ spin_lock_irqsave(&adapter->req_list_lock, flags);
+ fsf_req = zfcp_reqlist_find(adapter, req_id);
+
+ if (!fsf_req)
+ /*
+ * Unknown request means that we have potentially memory
+ * corruption and must stop the machine immediately.
+ */
+ panic("error: unknown req_id (%lx) on adapter %s.\n",
+ req_id, dev_name(&adapter->ccw_device->dev));
+
+ list_del(&fsf_req->list);
+ spin_unlock_irqrestore(&adapter->req_list_lock, flags);
+
+ fsf_req->queue_req.sbal_response = sbal_idx;
+ fsf_req->queue_req.qdio_inb_usage =
+ atomic_read(&qdio->resp_q.count);
+ zfcp_fsf_req_complete(fsf_req);
+
+ if (likely(sbale->flags & SBAL_FLAGS_LAST_ENTRY))
+ break;
+ }
+}
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index df7f232faba..dcc7c1dbcf5 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -3,13 +3,14 @@
*
* Interface to the FSF support functions.
*
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
*/
#ifndef FSF_H
#define FSF_H
#include <linux/pfn.h>
+#include <linux/scatterlist.h>
#define FSF_QTCB_CURRENT_VERSION 0x00000001
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
index e0a215309df..6c5228b627f 100644
--- a/drivers/s390/scsi/zfcp_qdio.c
+++ b/drivers/s390/scsi/zfcp_qdio.c
@@ -3,7 +3,7 @@
*
* Setup and helper functions to access QDIO.
*
- * Copyright IBM Corporation 2002, 2008
+ * Copyright IBM Corporation 2002, 2009
*/
#define KMSG_COMPONENT "zfcp"
@@ -34,29 +34,10 @@ zfcp_qdio_sbale(struct zfcp_qdio_queue *q, int sbal_idx, int sbale_idx)
return &q->sbal[sbal_idx]->element[sbale_idx];
}
-/**
- * zfcp_qdio_free - free memory used by request- and resposne queue
- * @adapter: pointer to the zfcp_adapter structure
- */
-void zfcp_qdio_free(struct zfcp_adapter *adapter)
+static void zfcp_qdio_handler_error(struct zfcp_qdio *qdio, char *id)
{
- struct qdio_buffer **sbal_req, **sbal_resp;
- int p;
-
- if (adapter->ccw_device)
- qdio_free(adapter->ccw_device);
-
- sbal_req = adapter->req_q.sbal;
- sbal_resp = adapter->resp_q.sbal;
-
- for (p = 0; p < QDIO_MAX_BUFFERS_PER_Q; p += QBUFF_PER_PAGE) {
- free_page((unsigned long) sbal_req[p]);
- free_page((unsigned long) sbal_resp[p]);
- }
-}
+ struct zfcp_adapter *adapter = qdio->adapter;
-static void zfcp_qdio_handler_error(struct zfcp_adapter *adapter, char *id)
-{
dev_warn(&adapter->ccw_device->dev, "A QDIO problem occurred\n");
zfcp_erp_adapter_reopen(adapter,
@@ -75,72 +56,47 @@ static void zfcp_qdio_zero_sbals(struct qdio_buffer *sbal[], int first, int cnt)
}
/* this needs to be called prior to updating the queue fill level */
-static void zfcp_qdio_account(struct zfcp_adapter *adapter)
+static inline void zfcp_qdio_account(struct zfcp_qdio *qdio)
{
- ktime_t now;
- s64 span;
+ unsigned long long now, span;
int free, used;
- spin_lock(&adapter->qdio_stat_lock);
- now = ktime_get();
- span = ktime_us_delta(now, adapter->req_q_time);
- free = max(0, atomic_read(&adapter->req_q.count));
+ spin_lock(&qdio->stat_lock);
+ now = get_clock_monotonic();
+ span = (now - qdio->req_q_time) >> 12;
+ free = atomic_read(&qdio->req_q.count);
used = QDIO_MAX_BUFFERS_PER_Q - free;
- adapter->req_q_util += used * span;
- adapter->req_q_time = now;
- spin_unlock(&adapter->qdio_stat_lock);
+ qdio->req_q_util += used * span;
+ qdio->req_q_time = now;
+ spin_unlock(&qdio->stat_lock);
}
static void zfcp_qdio_int_req(struct ccw_device *cdev, unsigned int qdio_err,
int queue_no, int first, int count,
unsigned long parm)
{
- struct zfcp_adapter *adapter = (struct zfcp_adapter *) parm;
- struct zfcp_qdio_queue *queue = &adapter->req_q;
+ struct zfcp_qdio *qdio = (struct zfcp_qdio *) parm;
+ struct zfcp_qdio_queue *queue = &qdio->req_q;
if (unlikely(qdio_err)) {
- zfcp_hba_dbf_event_qdio(adapter, qdio_err, first, count);
- zfcp_qdio_handler_error(adapter, "qdireq1");
+ zfcp_dbf_hba_qdio(qdio->adapter->dbf, qdio_err, first,
+ count);
+ zfcp_qdio_handler_error(qdio, "qdireq1");
return;
}
/* cleanup all SBALs being program-owned now */
zfcp_qdio_zero_sbals(queue->sbal, first, count);
- zfcp_qdio_account(adapter);
+ zfcp_qdio_account(qdio);
atomic_add(count, &queue->count);
- wake_up(&adapter->request_wq);
-}
-
-static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter,
- unsigned long req_id, int sbal_idx)
-{
- struct zfcp_fsf_req *fsf_req;
- unsigned long flags;
-
- spin_lock_irqsave(&adapter->req_list_lock, flags);
- fsf_req = zfcp_reqlist_find(adapter, req_id);
-
- if (!fsf_req)
- /*
- * Unknown request means that we have potentially memory
- * corruption and must stop the machine immediatly.
- */
- panic("error: unknown request id (%lx) on adapter %s.\n",
- req_id, dev_name(&adapter->ccw_device->dev));
-
- zfcp_reqlist_remove(adapter, fsf_req);
- spin_unlock_irqrestore(&adapter->req_list_lock, flags);
-
- fsf_req->sbal_response = sbal_idx;
- fsf_req->qdio_inb_usage = atomic_read(&adapter->resp_q.count);
- zfcp_fsf_req_complete(fsf_req);
+ wake_up(&qdio->req_q_wq);
}
-static void zfcp_qdio_resp_put_back(struct zfcp_adapter *adapter, int processed)
+static void zfcp_qdio_resp_put_back(struct zfcp_qdio *qdio, int processed)
{
- struct zfcp_qdio_queue *queue = &adapter->resp_q;
- struct ccw_device *cdev = adapter->ccw_device;
+ struct zfcp_qdio_queue *queue = &qdio->resp_q;
+ struct ccw_device *cdev = qdio->adapter->ccw_device;
u8 count, start = queue->first;
unsigned int retval;
@@ -162,14 +118,13 @@ static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err,
int queue_no, int first, int count,
unsigned long parm)
{
- struct zfcp_adapter *adapter = (struct zfcp_adapter *) parm;
- struct zfcp_qdio_queue *queue = &adapter->resp_q;
- struct qdio_buffer_element *sbale;
- int sbal_idx, sbale_idx, sbal_no;
+ struct zfcp_qdio *qdio = (struct zfcp_qdio *) parm;
+ int sbal_idx, sbal_no;
if (unlikely(qdio_err)) {
- zfcp_hba_dbf_event_qdio(adapter, qdio_err, first, count);
- zfcp_qdio_handler_error(adapter, "qdires1");
+ zfcp_dbf_hba_qdio(qdio->adapter->dbf, qdio_err, first,
+ count);
+ zfcp_qdio_handler_error(qdio, "qdires1");
return;
}
@@ -179,39 +134,27 @@ static void zfcp_qdio_int_resp(struct ccw_device *cdev, unsigned int qdio_err,
*/
for (sbal_no = 0; sbal_no < count; sbal_no++) {
sbal_idx = (first + sbal_no) % QDIO_MAX_BUFFERS_PER_Q;
-
/* go through all SBALEs of SBAL */
- for (sbale_idx = 0; sbale_idx < QDIO_MAX_ELEMENTS_PER_BUFFER;
- sbale_idx++) {
- sbale = zfcp_qdio_sbale(queue, sbal_idx, sbale_idx);
- zfcp_qdio_reqid_check(adapter,
- (unsigned long) sbale->addr,
- sbal_idx);
- if (likely(sbale->flags & SBAL_FLAGS_LAST_ENTRY))
- break;
- };
-
- if (unlikely(!(sbale->flags & SBAL_FLAGS_LAST_ENTRY)))
- dev_warn(&adapter->ccw_device->dev,
- "A QDIO protocol error occurred, "
- "operations continue\n");
+ zfcp_fsf_reqid_check(qdio, sbal_idx);
}
/*
* put range of SBALs back to response queue
* (including SBALs which have already been free before)
*/
- zfcp_qdio_resp_put_back(adapter, count);
+ zfcp_qdio_resp_put_back(qdio, count);
}
/**
* zfcp_qdio_sbale_req - return ptr to SBALE of req_q for a struct zfcp_fsf_req
- * @fsf_req: pointer to struct fsf_req
+ * @qdio: pointer to struct zfcp_qdio
+ * @q_rec: pointer to struct zfcp_queue_rec
* Returns: pointer to qdio_buffer_element (SBALE) structure
*/
-struct qdio_buffer_element *zfcp_qdio_sbale_req(struct zfcp_fsf_req *req)
+struct qdio_buffer_element *zfcp_qdio_sbale_req(struct zfcp_qdio *qdio,
+ struct zfcp_queue_req *q_req)
{
- return zfcp_qdio_sbale(&req->adapter->req_q, req->sbal_last, 0);
+ return zfcp_qdio_sbale(&qdio->req_q, q_req->sbal_last, 0);
}
/**
@@ -219,74 +162,80 @@ struct qdio_buffer_element *zfcp_qdio_sbale_req(struct zfcp_fsf_req *req)
* @fsf_req: pointer to struct fsf_req
* Returns: pointer to qdio_buffer_element (SBALE) structure
*/
-struct qdio_buffer_element *zfcp_qdio_sbale_curr(struct zfcp_fsf_req *req)
+struct qdio_buffer_element *zfcp_qdio_sbale_curr(struct zfcp_qdio *qdio,
+ struct zfcp_queue_req *q_req)
{
- return zfcp_qdio_sbale(&req->adapter->req_q, req->sbal_last,
- req->sbale_curr);
+ return zfcp_qdio_sbale(&qdio->req_q, q_req->sbal_last,
+ q_req->sbale_curr);
}
-static void zfcp_qdio_sbal_limit(struct zfcp_fsf_req *fsf_req, int max_sbals)
+static void zfcp_qdio_sbal_limit(struct zfcp_qdio *qdio,
+ struct zfcp_queue_req *q_req, int max_sbals)
{
- int count = atomic_read(&fsf_req->adapter->req_q.count);
+ int count = atomic_read(&qdio->req_q.count);
count = min(count, max_sbals);
- fsf_req->sbal_limit = (fsf_req->sbal_first + count - 1)
+ q_req->sbal_limit = (q_req->sbal_first + count - 1)
% QDIO_MAX_BUFFERS_PER_Q;
}
static struct qdio_buffer_element *
-zfcp_qdio_sbal_chain(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
+zfcp_qdio_sbal_chain(struct zfcp_qdio *qdio, struct zfcp_queue_req *q_req,
+ unsigned long sbtype)
{
struct qdio_buffer_element *sbale;
/* set last entry flag in current SBALE of current SBAL */
- sbale = zfcp_qdio_sbale_curr(fsf_req);
+ sbale = zfcp_qdio_sbale_curr(qdio, q_req);
sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
/* don't exceed last allowed SBAL */
- if (fsf_req->sbal_last == fsf_req->sbal_limit)
+ if (q_req->sbal_last == q_req->sbal_limit)
return NULL;
/* set chaining flag in first SBALE of current SBAL */
- sbale = zfcp_qdio_sbale_req(fsf_req);
+ sbale = zfcp_qdio_sbale_req(qdio, q_req);
sbale->flags |= SBAL_FLAGS0_MORE_SBALS;
/* calculate index of next SBAL */
- fsf_req->sbal_last++;
- fsf_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
+ q_req->sbal_last++;
+ q_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
/* keep this requests number of SBALs up-to-date */
- fsf_req->sbal_number++;
+ q_req->sbal_number++;
/* start at first SBALE of new SBAL */
- fsf_req->sbale_curr = 0;
+ q_req->sbale_curr = 0;
/* set storage-block type for new SBAL */
- sbale = zfcp_qdio_sbale_curr(fsf_req);
+ sbale = zfcp_qdio_sbale_curr(qdio, q_req);
sbale->flags |= sbtype;
return sbale;
}
static struct qdio_buffer_element *
-zfcp_qdio_sbale_next(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
+zfcp_qdio_sbale_next(struct zfcp_qdio *qdio, struct zfcp_queue_req *q_req,
+ unsigned int sbtype)
{
- if (fsf_req->sbale_curr == ZFCP_LAST_SBALE_PER_SBAL)
- return zfcp_qdio_sbal_chain(fsf_req, sbtype);
- fsf_req->sbale_curr++;
- return zfcp_qdio_sbale_curr(fsf_req);
+ if (q_req->sbale_curr == ZFCP_LAST_SBALE_PER_SBAL)
+ return zfcp_qdio_sbal_chain(qdio, q_req, sbtype);
+ q_req->sbale_curr++;
+ return zfcp_qdio_sbale_curr(qdio, q_req);
}
-static void zfcp_qdio_undo_sbals(struct zfcp_fsf_req *fsf_req)
+static void zfcp_qdio_undo_sbals(struct zfcp_qdio *qdio,
+ struct zfcp_queue_req *q_req)
{
- struct qdio_buffer **sbal = fsf_req->adapter->req_q.sbal;
- int first = fsf_req->sbal_first;
- int last = fsf_req->sbal_last;
+ struct qdio_buffer **sbal = qdio->req_q.sbal;
+ int first = q_req->sbal_first;
+ int last = q_req->sbal_last;
int count = (last - first + QDIO_MAX_BUFFERS_PER_Q) %
QDIO_MAX_BUFFERS_PER_Q + 1;
zfcp_qdio_zero_sbals(sbal, first, count);
}
-static int zfcp_qdio_fill_sbals(struct zfcp_fsf_req *fsf_req,
+static int zfcp_qdio_fill_sbals(struct zfcp_qdio *qdio,
+ struct zfcp_queue_req *q_req,
unsigned int sbtype, void *start_addr,
unsigned int total_length)
{
@@ -297,10 +246,10 @@ static int zfcp_qdio_fill_sbals(struct zfcp_fsf_req *fsf_req,
/* split segment up */
for (addr = start_addr, remaining = total_length; remaining > 0;
addr += length, remaining -= length) {
- sbale = zfcp_qdio_sbale_next(fsf_req, sbtype);
+ sbale = zfcp_qdio_sbale_next(qdio, q_req, sbtype);
if (!sbale) {
- atomic_inc(&fsf_req->adapter->qdio_outb_full);
- zfcp_qdio_undo_sbals(fsf_req);
+ atomic_inc(&qdio->req_q_full);
+ zfcp_qdio_undo_sbals(qdio, q_req);
return -EINVAL;
}
@@ -322,29 +271,31 @@ static int zfcp_qdio_fill_sbals(struct zfcp_fsf_req *fsf_req,
* @max_sbals: upper bound for number of SBALs to be used
* Returns: number of bytes, or error (negativ)
*/
-int zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
- struct scatterlist *sg, int max_sbals)
+int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio,
+ struct zfcp_queue_req *q_req,
+ unsigned long sbtype, struct scatterlist *sg,
+ int max_sbals)
{
struct qdio_buffer_element *sbale;
int retval, bytes = 0;
/* figure out last allowed SBAL */
- zfcp_qdio_sbal_limit(fsf_req, max_sbals);
+ zfcp_qdio_sbal_limit(qdio, q_req, max_sbals);
/* set storage-block type for this request */
- sbale = zfcp_qdio_sbale_req(fsf_req);
+ sbale = zfcp_qdio_sbale_req(qdio, q_req);
sbale->flags |= sbtype;
for (; sg; sg = sg_next(sg)) {
- retval = zfcp_qdio_fill_sbals(fsf_req, sbtype, sg_virt(sg),
- sg->length);
+ retval = zfcp_qdio_fill_sbals(qdio, q_req, sbtype,
+ sg_virt(sg), sg->length);
if (retval < 0)
return retval;
bytes += sg->length;
}
/* assume that no other SBALEs are to follow in the same SBAL */
- sbale = zfcp_qdio_sbale_curr(fsf_req);
+ sbale = zfcp_qdio_sbale_curr(qdio, q_req);
sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
return bytes;
@@ -352,21 +303,22 @@ int zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
/**
* zfcp_qdio_send - set PCI flag in first SBALE and send req to QDIO
- * @fsf_req: pointer to struct zfcp_fsf_req
+ * @qdio: pointer to struct zfcp_qdio
+ * @q_req: pointer to struct zfcp_queue_req
* Returns: 0 on success, error otherwise
*/
-int zfcp_qdio_send(struct zfcp_fsf_req *fsf_req)
+int zfcp_qdio_send(struct zfcp_qdio *qdio, struct zfcp_queue_req *q_req)
{
- struct zfcp_adapter *adapter = fsf_req->adapter;
- struct zfcp_qdio_queue *req_q = &adapter->req_q;
- int first = fsf_req->sbal_first;
- int count = fsf_req->sbal_number;
+ struct zfcp_qdio_queue *req_q = &qdio->req_q;
+ int first = q_req->sbal_first;
+ int count = q_req->sbal_number;
int retval;
unsigned int qdio_flags = QDIO_FLAG_SYNC_OUTPUT;
- zfcp_qdio_account(adapter);
+ zfcp_qdio_account(qdio);
- retval = do_QDIO(adapter->ccw_device, qdio_flags, 0, first, count);
+ retval = do_QDIO(qdio->adapter->ccw_device, qdio_flags, 0, first,
+ count);
if (unlikely(retval)) {
zfcp_qdio_zero_sbals(req_q->sbal, first, count);
return retval;
@@ -379,63 +331,69 @@ int zfcp_qdio_send(struct zfcp_fsf_req *fsf_req)
return 0;
}
+
+static void zfcp_qdio_setup_init_data(struct qdio_initialize *id,
+ struct zfcp_qdio *qdio)
+{
+
+ id->cdev = qdio->adapter->ccw_device;
+ id->q_format = QDIO_ZFCP_QFMT;
+ memcpy(id->adapter_name, dev_name(&id->cdev->dev), 8);
+ ASCEBC(id->adapter_name, 8);
+ id->qib_param_field_format = 0;
+ id->qib_param_field = NULL;
+ id->input_slib_elements = NULL;
+ id->output_slib_elements = NULL;
+ id->no_input_qs = 1;
+ id->no_output_qs = 1;
+ id->input_handler = zfcp_qdio_int_resp;
+ id->output_handler = zfcp_qdio_int_req;
+ id->int_parm = (unsigned long) qdio;
+ id->flags = QDIO_INBOUND_0COPY_SBALS |
+ QDIO_OUTBOUND_0COPY_SBALS | QDIO_USE_OUTBOUND_PCIS;
+ id->input_sbal_addr_array = (void **) (qdio->resp_q.sbal);
+ id->output_sbal_addr_array = (void **) (qdio->req_q.sbal);
+
+}
/**
* zfcp_qdio_allocate - allocate queue memory and initialize QDIO data
* @adapter: pointer to struct zfcp_adapter
* Returns: -ENOMEM on memory allocation error or return value from
* qdio_allocate
*/
-int zfcp_qdio_allocate(struct zfcp_adapter *adapter)
+static int zfcp_qdio_allocate(struct zfcp_qdio *qdio)
{
- struct qdio_initialize *init_data;
+ struct qdio_initialize init_data;
- if (zfcp_qdio_buffers_enqueue(adapter->req_q.sbal) ||
- zfcp_qdio_buffers_enqueue(adapter->resp_q.sbal))
+ if (zfcp_qdio_buffers_enqueue(qdio->req_q.sbal) ||
+ zfcp_qdio_buffers_enqueue(qdio->resp_q.sbal))
return -ENOMEM;
- init_data = &adapter->qdio_init_data;
-
- init_data->cdev = adapter->ccw_device;
- init_data->q_format = QDIO_ZFCP_QFMT;
- memcpy(init_data->adapter_name, dev_name(&adapter->ccw_device->dev), 8);
- ASCEBC(init_data->adapter_name, 8);
- init_data->qib_param_field_format = 0;
- init_data->qib_param_field = NULL;
- init_data->input_slib_elements = NULL;
- init_data->output_slib_elements = NULL;
- init_data->no_input_qs = 1;
- init_data->no_output_qs = 1;
- init_data->input_handler = zfcp_qdio_int_resp;
- init_data->output_handler = zfcp_qdio_int_req;
- init_data->int_parm = (unsigned long) adapter;
- init_data->flags = QDIO_INBOUND_0COPY_SBALS |
- QDIO_OUTBOUND_0COPY_SBALS | QDIO_USE_OUTBOUND_PCIS;
- init_data->input_sbal_addr_array =
- (void **) (adapter->resp_q.sbal);
- init_data->output_sbal_addr_array =
- (void **) (adapter->req_q.sbal);
-
- return qdio_allocate(init_data);
+ zfcp_qdio_setup_init_data(&init_data, qdio);
+
+ return qdio_allocate(&init_data);
}
/**
* zfcp_close_qdio - close qdio queues for an adapter
+ * @qdio: pointer to structure zfcp_qdio
*/
-void zfcp_qdio_close(struct zfcp_adapter *adapter)
+void zfcp_qdio_close(struct zfcp_qdio *qdio)
{
struct zfcp_qdio_queue *req_q;
int first, count;
- if (!(atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))
+ if (!(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))
return;
/* clear QDIOUP flag, thus do_QDIO is not called during qdio_shutdown */
- req_q = &adapter->req_q;
- spin_lock_bh(&adapter->req_q_lock);
- atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &adapter->status);
- spin_unlock_bh(&adapter->req_q_lock);
+ req_q = &qdio->req_q;
+ spin_lock_bh(&qdio->req_q_lock);
+ atomic_clear_mask(ZFCP_STATUS_ADAPTER_QDIOUP, &qdio->adapter->status);
+ spin_unlock_bh(&qdio->req_q_lock);
- qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR);
+ qdio_shutdown(qdio->adapter->ccw_device,
+ QDIO_FLAG_CLEANUP_USING_CLEAR);
/* cleanup used outbound sbals */
count = atomic_read(&req_q->count);
@@ -446,50 +404,99 @@ void zfcp_qdio_close(struct zfcp_adapter *adapter)
}
req_q->first = 0;
atomic_set(&req_q->count, 0);
- adapter->resp_q.first = 0;
- atomic_set(&adapter->resp_q.count, 0);
+ qdio->resp_q.first = 0;
+ atomic_set(&qdio->resp_q.count, 0);
}
/**
* zfcp_qdio_open - prepare and initialize response queue
- * @adapter: pointer to struct zfcp_adapter
+ * @qdio: pointer to struct zfcp_qdio
* Returns: 0 on success, otherwise -EIO
*/
-int zfcp_qdio_open(struct zfcp_adapter *adapter)
+int zfcp_qdio_open(struct zfcp_qdio *qdio)
{
struct qdio_buffer_element *sbale;
+ struct qdio_initialize init_data;
+ struct ccw_device *cdev = qdio->adapter->ccw_device;
int cc;
- if (atomic_read(&adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)
+ if (atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP)
return -EIO;
- if (qdio_establish(&adapter->qdio_init_data))
+ zfcp_qdio_setup_init_data(&init_data, qdio);
+
+ if (qdio_establish(&init_data))
goto failed_establish;
- if (qdio_activate(adapter->ccw_device))
+ if (qdio_activate(cdev))
goto failed_qdio;
for (cc = 0; cc < QDIO_MAX_BUFFERS_PER_Q; cc++) {
- sbale = &(adapter->resp_q.sbal[cc]->element[0]);
+ sbale = &(qdio->resp_q.sbal[cc]->element[0]);
sbale->length = 0;
sbale->flags = SBAL_FLAGS_LAST_ENTRY;
sbale->addr = NULL;
}
- if (do_QDIO(adapter->ccw_device, QDIO_FLAG_SYNC_INPUT, 0, 0,
+ if (do_QDIO(cdev, QDIO_FLAG_SYNC_INPUT, 0, 0,
QDIO_MAX_BUFFERS_PER_Q))
goto failed_qdio;
/* set index of first avalable SBALS / number of available SBALS */
- adapter->req_q.first = 0;
- atomic_set(&adapter->req_q.count, QDIO_MAX_BUFFERS_PER_Q);
+ qdio->req_q.first = 0;
+ atomic_set(&qdio->req_q.count, QDIO_MAX_BUFFERS_PER_Q);
return 0;
failed_qdio:
- qdio_shutdown(adapter->ccw_device, QDIO_FLAG_CLEANUP_USING_CLEAR);
+ qdio_shutdown(cdev, QDIO_FLAG_CLEANUP_USING_CLEAR);
failed_establish:
- dev_err(&adapter->ccw_device->dev,
+ dev_err(&cdev->dev,
"Setting up the QDIO connection to the FCP adapter failed\n");
return -EIO;
}
+
+void zfcp_qdio_destroy(struct zfcp_qdio *qdio)
+{
+ struct qdio_buffer **sbal_req, **sbal_resp;
+ int p;
+
+ if (!qdio)
+ return;
+
+ if (qdio->adapter->ccw_device)
+ qdio_free(qdio->adapter->ccw_device);
+
+ sbal_req = qdio->req_q.sbal;
+ sbal_resp = qdio->resp_q.sbal;
+
+ for (p = 0; p < QDIO_MAX_BUFFERS_PER_Q; p += QBUFF_PER_PAGE) {
+ free_page((unsigned long) sbal_req[p]);
+ free_page((unsigned long) sbal_resp[p]);
+ }
+
+ kfree(qdio);
+}
+
+int zfcp_qdio_setup(struct zfcp_adapter *adapter)
+{
+ struct zfcp_qdio *qdio;
+
+ qdio = kzalloc(sizeof(struct zfcp_qdio), GFP_KERNEL);
+ if (!qdio)
+ return -ENOMEM;
+
+ qdio->adapter = adapter;
+
+ if (zfcp_qdio_allocate(qdio)) {
+ zfcp_qdio_destroy(qdio);
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&qdio->req_q_lock);
+ spin_lock_init(&qdio->stat_lock);
+
+ adapter->qdio = qdio;
+ return 0;
+}
+
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 6925a178468..3ff726afafc 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -9,8 +9,9 @@
#define KMSG_COMPONENT "zfcp"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
-#include "zfcp_ext.h"
#include <asm/atomic.h>
+#include "zfcp_ext.h"
+#include "zfcp_dbf.h"
static unsigned int default_depth = 32;
module_param_named(queue_depth, default_depth, uint, 0600);
@@ -52,11 +53,11 @@ static int zfcp_scsi_slave_configure(struct scsi_device *sdp)
static void zfcp_scsi_command_fail(struct scsi_cmnd *scpnt, int result)
{
+ struct zfcp_adapter *adapter =
+ (struct zfcp_adapter *) scpnt->device->host->hostdata[0];
set_host_byte(scpnt, result);
if ((scpnt->device != NULL) && (scpnt->device->host != NULL))
- zfcp_scsi_dbf_event_result("fail", 4,
- (struct zfcp_adapter*) scpnt->device->host->hostdata[0],
- scpnt, NULL);
+ zfcp_dbf_scsi_result("fail", 4, adapter->dbf, scpnt, NULL);
/* return directly */
scpnt->scsi_done(scpnt);
}
@@ -92,7 +93,7 @@ static int zfcp_scsi_queuecommand(struct scsi_cmnd *scpnt,
scsi_result = fc_remote_port_chkready(rport);
if (unlikely(scsi_result)) {
scpnt->result = scsi_result;
- zfcp_scsi_dbf_event_result("fail", 4, adapter, scpnt, NULL);
+ zfcp_dbf_scsi_result("fail", 4, adapter->dbf, scpnt, NULL);
scpnt->scsi_done(scpnt);
return 0;
}
@@ -180,8 +181,8 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
spin_unlock(&adapter->req_list_lock);
if (!old_req) {
write_unlock_irqrestore(&adapter->abort_lock, flags);
- zfcp_scsi_dbf_event_abort("lte1", adapter, scpnt, NULL,
- old_reqid);
+ zfcp_dbf_scsi_abort("lte1", adapter->dbf, scpnt, NULL,
+ old_reqid);
return FAILED; /* completion could be in progress */
}
old_req->data = NULL;
@@ -197,16 +198,15 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
zfcp_erp_wait(adapter);
if (!(atomic_read(&adapter->status) &
ZFCP_STATUS_COMMON_RUNNING)) {
- zfcp_scsi_dbf_event_abort("nres", adapter, scpnt, NULL,
- old_reqid);
+ zfcp_dbf_scsi_abort("nres", adapter->dbf, scpnt, NULL,
+ old_reqid);
return SUCCESS;
}
}
if (!abrt_req)
return FAILED;
- wait_event(abrt_req->completion_wq,
- abrt_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ wait_for_completion(&abrt_req->completion);
if (abrt_req->status & ZFCP_STATUS_FSFREQ_ABORTSUCCEEDED)
dbf_tag = "okay";
@@ -216,7 +216,7 @@ static int zfcp_scsi_eh_abort_handler(struct scsi_cmnd *scpnt)
dbf_tag = "fail";
retval = FAILED;
}
- zfcp_scsi_dbf_event_abort(dbf_tag, adapter, scpnt, abrt_req, old_reqid);
+ zfcp_dbf_scsi_abort(dbf_tag, adapter->dbf, scpnt, abrt_req, old_reqid);
zfcp_fsf_req_free(abrt_req);
return retval;
}
@@ -225,7 +225,7 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
{
struct zfcp_unit *unit = scpnt->device->hostdata;
struct zfcp_adapter *adapter = unit->port->adapter;
- struct zfcp_fsf_req *fsf_req;
+ struct zfcp_fsf_req *fsf_req = NULL;
int retval = SUCCESS;
int retry = 3;
@@ -237,25 +237,23 @@ static int zfcp_task_mgmt_function(struct scsi_cmnd *scpnt, u8 tm_flags)
zfcp_erp_wait(adapter);
if (!(atomic_read(&adapter->status) &
ZFCP_STATUS_COMMON_RUNNING)) {
- zfcp_scsi_dbf_event_devreset("nres", tm_flags, unit,
- scpnt);
+ zfcp_dbf_scsi_devreset("nres", tm_flags, unit, scpnt);
return SUCCESS;
}
}
if (!fsf_req)
return FAILED;
- wait_event(fsf_req->completion_wq,
- fsf_req->status & ZFCP_STATUS_FSFREQ_COMPLETED);
+ wait_for_completion(&fsf_req->completion);
if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCFAILED) {
- zfcp_scsi_dbf_event_devreset("fail", tm_flags, unit, scpnt);
+ zfcp_dbf_scsi_devreset("fail", tm_flags, unit, scpnt);
retval = FAILED;
} else if (fsf_req->status & ZFCP_STATUS_FSFREQ_TMFUNCNOTSUPP) {
- zfcp_scsi_dbf_event_devreset("nsup", tm_flags, unit, scpnt);
+ zfcp_dbf_scsi_devreset("nsup", tm_flags, unit, scpnt);
retval = FAILED;
} else
- zfcp_scsi_dbf_event_devreset("okay", tm_flags, unit, scpnt);
+ zfcp_dbf_scsi_devreset("okay", tm_flags, unit, scpnt);
zfcp_fsf_req_free(fsf_req);
return retval;
@@ -430,7 +428,7 @@ static struct fc_host_statistics *zfcp_get_fc_host_stats(struct Scsi_Host *host)
if (!data)
return NULL;
- ret = zfcp_fsf_exchange_port_data_sync(adapter, data);
+ ret = zfcp_fsf_exchange_port_data_sync(adapter->qdio, data);
if (ret) {
kfree(data);
return NULL;
@@ -459,7 +457,7 @@ static void zfcp_reset_fc_host_stats(struct Scsi_Host *shost)
if (!data)
return;
- ret = zfcp_fsf_exchange_port_data_sync(adapter, data);
+ ret = zfcp_fsf_exchange_port_data_sync(adapter->qdio, data);
if (ret)
kfree(data);
else {
@@ -493,21 +491,6 @@ static void zfcp_set_rport_dev_loss_tmo(struct fc_rport *rport, u32 timeout)
}
/**
- * zfcp_scsi_dev_loss_tmo_callbk - Free any reference to rport
- * @rport: The rport that is about to be deleted.
- */
-static void zfcp_scsi_dev_loss_tmo_callbk(struct fc_rport *rport)
-{
- struct zfcp_port *port;
-
- write_lock_irq(&zfcp_data.config_lock);
- port = rport->dd_data;
- if (port)
- port->rport = NULL;
- write_unlock_irq(&zfcp_data.config_lock);
-}
-
-/**
* zfcp_scsi_terminate_rport_io - Terminate all I/O on a rport
* @rport: The FC rport where to teminate I/O
*
@@ -518,9 +501,12 @@ static void zfcp_scsi_dev_loss_tmo_callbk(struct fc_rport *rport)
static void zfcp_scsi_terminate_rport_io(struct fc_rport *rport)
{
struct zfcp_port *port;
+ struct Scsi_Host *shost = rport_to_shost(rport);
+ struct zfcp_adapter *adapter =
+ (struct zfcp_adapter *)shost->hostdata[0];
write_lock_irq(&zfcp_data.config_lock);
- port = rport->dd_data;
+ port = zfcp_get_port_by_wwpn(adapter, rport->port_name);
if (port)
zfcp_port_get(port);
write_unlock_irq(&zfcp_data.config_lock);
@@ -552,7 +538,6 @@ static void zfcp_scsi_rport_register(struct zfcp_port *port)
return;
}
- rport->dd_data = port;
rport->maxframe_size = port->maxframe_size;
rport->supported_classes = port->supported_classes;
port->rport = rport;
@@ -573,7 +558,7 @@ void zfcp_scsi_schedule_rport_register(struct zfcp_port *port)
zfcp_port_get(port);
port->rport_task = RPORT_ADD;
- if (!queue_work(zfcp_data.work_queue, &port->rport_work))
+ if (!queue_work(port->adapter->work_queue, &port->rport_work))
zfcp_port_put(port);
}
@@ -582,8 +567,11 @@ void zfcp_scsi_schedule_rport_block(struct zfcp_port *port)
zfcp_port_get(port);
port->rport_task = RPORT_DEL;
- if (!queue_work(zfcp_data.work_queue, &port->rport_work))
- zfcp_port_put(port);
+ if (port->rport && queue_work(port->adapter->work_queue,
+ &port->rport_work))
+ return;
+
+ zfcp_port_put(port);
}
void zfcp_scsi_schedule_rports_block(struct zfcp_adapter *adapter)
@@ -662,7 +650,6 @@ struct fc_function_template zfcp_transport_functions = {
.reset_fc_host_stats = zfcp_reset_fc_host_stats,
.set_rport_dev_loss_tmo = zfcp_set_rport_dev_loss_tmo,
.get_host_port_state = zfcp_get_host_port_state,
- .dev_loss_tmo_callbk = zfcp_scsi_dev_loss_tmo_callbk,
.terminate_rport_io = zfcp_scsi_terminate_rport_io,
.show_host_port_state = 1,
.bsg_request = zfcp_execute_fc_job,
diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c
index 0fe5cce818c..079a8cf518a 100644
--- a/drivers/s390/scsi/zfcp_sysfs.c
+++ b/drivers/s390/scsi/zfcp_sysfs.c
@@ -88,7 +88,7 @@ static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev, \
unsigned long val; \
int retval = 0; \
\
- down(&zfcp_data.config_sema); \
+ mutex_lock(&zfcp_data.config_mutex); \
if (atomic_read(&_feat->status) & ZFCP_STATUS_COMMON_REMOVE) { \
retval = -EBUSY; \
goto out; \
@@ -105,7 +105,7 @@ static ssize_t zfcp_sysfs_##_feat##_failed_store(struct device *dev, \
_reopen_id, NULL); \
zfcp_erp_wait(_adapter); \
out: \
- up(&zfcp_data.config_sema); \
+ mutex_unlock(&zfcp_data.config_mutex); \
return retval ? retval : (ssize_t) count; \
} \
static ZFCP_DEV_ATTR(_feat, failed, S_IWUSR | S_IRUGO, \
@@ -126,7 +126,7 @@ static ssize_t zfcp_sysfs_port_rescan_store(struct device *dev,
if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE)
return -EBUSY;
- ret = zfcp_scan_ports(adapter);
+ ret = zfcp_fc_scan_ports(adapter);
return ret ? ret : (ssize_t) count;
}
static ZFCP_DEV_ATTR(adapter, port_rescan, S_IWUSR, NULL,
@@ -142,7 +142,7 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
int retval = 0;
LIST_HEAD(port_remove_lh);
- down(&zfcp_data.config_sema);
+ mutex_lock(&zfcp_data.config_mutex);
if (atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_REMOVE) {
retval = -EBUSY;
goto out;
@@ -173,7 +173,7 @@ static ssize_t zfcp_sysfs_port_remove_store(struct device *dev,
zfcp_port_put(port);
zfcp_port_dequeue(port);
out:
- up(&zfcp_data.config_sema);
+ mutex_unlock(&zfcp_data.config_mutex);
return retval ? retval : (ssize_t) count;
}
static ZFCP_DEV_ATTR(adapter, port_remove, S_IWUSR, NULL,
@@ -207,7 +207,7 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
u64 fcp_lun;
int retval = -EINVAL;
- down(&zfcp_data.config_sema);
+ mutex_lock(&zfcp_data.config_mutex);
if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
retval = -EBUSY;
goto out;
@@ -226,7 +226,7 @@ static ssize_t zfcp_sysfs_unit_add_store(struct device *dev,
zfcp_erp_wait(unit->port->adapter);
zfcp_unit_put(unit);
out:
- up(&zfcp_data.config_sema);
+ mutex_unlock(&zfcp_data.config_mutex);
return retval ? retval : (ssize_t) count;
}
static DEVICE_ATTR(unit_add, S_IWUSR, NULL, zfcp_sysfs_unit_add_store);
@@ -241,7 +241,7 @@ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
int retval = 0;
LIST_HEAD(unit_remove_lh);
- down(&zfcp_data.config_sema);
+ mutex_lock(&zfcp_data.config_mutex);
if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
retval = -EBUSY;
goto out;
@@ -282,7 +282,7 @@ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
zfcp_unit_put(unit);
zfcp_unit_dequeue(unit);
out:
- up(&zfcp_data.config_sema);
+ mutex_unlock(&zfcp_data.config_mutex);
return retval ? retval : (ssize_t) count;
}
static DEVICE_ATTR(unit_remove, S_IWUSR, NULL, zfcp_sysfs_unit_remove_store);
@@ -425,7 +425,7 @@ static ssize_t zfcp_sysfs_adapter_util_show(struct device *dev,
if (!qtcb_port)
return -ENOMEM;
- retval = zfcp_fsf_exchange_port_data_sync(adapter, qtcb_port);
+ retval = zfcp_fsf_exchange_port_data_sync(adapter->qdio, qtcb_port);
if (!retval)
retval = sprintf(buf, "%u %u %u\n", qtcb_port->cp_util,
qtcb_port->cb_util, qtcb_port->a_util);
@@ -451,7 +451,7 @@ static int zfcp_sysfs_adapter_ex_config(struct device *dev,
if (!qtcb_config)
return -ENOMEM;
- retval = zfcp_fsf_exchange_config_data_sync(adapter, qtcb_config);
+ retval = zfcp_fsf_exchange_config_data_sync(adapter->qdio, qtcb_config);
if (!retval)
*stat_inf = qtcb_config->stat_info;
@@ -492,15 +492,15 @@ static ssize_t zfcp_sysfs_adapter_q_full_show(struct device *dev,
char *buf)
{
struct Scsi_Host *scsi_host = class_to_shost(dev);
- struct zfcp_adapter *adapter =
- (struct zfcp_adapter *) scsi_host->hostdata[0];
+ struct zfcp_qdio *qdio =
+ ((struct zfcp_adapter *) scsi_host->hostdata[0])->qdio;
u64 util;
- spin_lock_bh(&adapter->qdio_stat_lock);
- util = adapter->req_q_util;
- spin_unlock_bh(&adapter->qdio_stat_lock);
+ spin_lock_bh(&qdio->stat_lock);
+ util = qdio->req_q_util;
+ spin_unlock_bh(&qdio->stat_lock);
- return sprintf(buf, "%d %llu\n", atomic_read(&adapter->qdio_outb_full),
+ return sprintf(buf, "%d %llu\n", atomic_read(&qdio->req_q_full),
(unsigned long long)util);
}
static DEVICE_ATTR(queue_full, S_IRUGO, zfcp_sysfs_adapter_q_full_show, NULL);
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 9c23122f755..82bb3b2d207 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1811,6 +1811,12 @@ config ZFCP
called zfcp. If you want to compile it as a module, say M here
and read <file:Documentation/kbuild/modules.txt>.
+config SCSI_PMCRAID
+ tristate "PMC SIERRA Linux MaxRAID adapter support"
+ depends on PCI && SCSI
+ ---help---
+ This driver supports the PMC SIERRA MaxRAID adapters.
+
config SCSI_SRP
tristate "SCSI RDMA Protocol helper library"
depends on SCSI && PCI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 25429ea63d0..61a94af3cee 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -130,6 +130,7 @@ obj-$(CONFIG_SCSI_MVSAS) += mvsas/
obj-$(CONFIG_PS3_ROM) += ps3rom.o
obj-$(CONFIG_SCSI_CXGB3_ISCSI) += libiscsi.o libiscsi_tcp.o cxgb3i/
obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/
+obj-$(CONFIG_SCSI_PMCRAID) += pmcraid.o
obj-$(CONFIG_ARM) += arm/
diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c
index ae4b2d588fd..0c4210d48ee 100644
--- a/drivers/scsi/bnx2i/bnx2i_init.c
+++ b/drivers/scsi/bnx2i/bnx2i_init.c
@@ -15,11 +15,10 @@
static struct list_head adapter_list = LIST_HEAD_INIT(adapter_list);
static u32 adapter_count;
-static int bnx2i_reg_device;
#define DRV_MODULE_NAME "bnx2i"
-#define DRV_MODULE_VERSION "2.0.1d"
-#define DRV_MODULE_RELDATE "Mar 25, 2009"
+#define DRV_MODULE_VERSION "2.0.1e"
+#define DRV_MODULE_RELDATE "June 22, 2009"
static char version[] __devinitdata =
"Broadcom NetXtreme II iSCSI Driver " DRV_MODULE_NAME \
@@ -31,7 +30,7 @@ MODULE_DESCRIPTION("Broadcom NetXtreme II BCM5706/5708/5709 iSCSI Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_MODULE_VERSION);
-static DEFINE_RWLOCK(bnx2i_dev_lock);
+static DEFINE_MUTEX(bnx2i_dev_lock);
unsigned int event_coal_div = 1;
module_param(event_coal_div, int, 0664);
@@ -100,14 +99,14 @@ struct bnx2i_hba *get_adapter_list_head(void)
if (!adapter_count)
goto hba_not_found;
- read_lock(&bnx2i_dev_lock);
+ mutex_lock(&bnx2i_dev_lock);
list_for_each_entry(tmp_hba, &adapter_list, link) {
if (tmp_hba->cnic && tmp_hba->cnic->cm_select_dev) {
hba = tmp_hba;
break;
}
}
- read_unlock(&bnx2i_dev_lock);
+ mutex_unlock(&bnx2i_dev_lock);
hba_not_found:
return hba;
}
@@ -122,14 +121,14 @@ struct bnx2i_hba *bnx2i_find_hba_for_cnic(struct cnic_dev *cnic)
{
struct bnx2i_hba *hba, *temp;
- read_lock(&bnx2i_dev_lock);
+ mutex_lock(&bnx2i_dev_lock);
list_for_each_entry_safe(hba, temp, &adapter_list, link) {
if (hba->cnic == cnic) {
- read_unlock(&bnx2i_dev_lock);
+ mutex_unlock(&bnx2i_dev_lock);
return hba;
}
}
- read_unlock(&bnx2i_dev_lock);
+ mutex_unlock(&bnx2i_dev_lock);
return NULL;
}
@@ -186,18 +185,17 @@ void bnx2i_stop(void *handle)
*/
void bnx2i_register_device(struct bnx2i_hba *hba)
{
+ int rc;
+
if (test_bit(ADAPTER_STATE_GOING_DOWN, &hba->adapter_state) ||
test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
return;
}
- hba->cnic->register_device(hba->cnic, CNIC_ULP_ISCSI, hba);
+ rc = hba->cnic->register_device(hba->cnic, CNIC_ULP_ISCSI, hba);
- spin_lock(&hba->lock);
- bnx2i_reg_device++;
- spin_unlock(&hba->lock);
-
- set_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
+ if (!rc)
+ set_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
}
@@ -211,10 +209,10 @@ void bnx2i_reg_dev_all(void)
{
struct bnx2i_hba *hba, *temp;
- read_lock(&bnx2i_dev_lock);
+ mutex_lock(&bnx2i_dev_lock);
list_for_each_entry_safe(hba, temp, &adapter_list, link)
bnx2i_register_device(hba);
- read_unlock(&bnx2i_dev_lock);
+ mutex_unlock(&bnx2i_dev_lock);
}
@@ -234,10 +232,6 @@ static void bnx2i_unreg_one_device(struct bnx2i_hba *hba)
hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI);
- spin_lock(&hba->lock);
- bnx2i_reg_device--;
- spin_unlock(&hba->lock);
-
/* ep_disconnect could come before NETDEV_DOWN, driver won't
* see NETDEV_DOWN as it already unregistered itself.
*/
@@ -255,10 +249,10 @@ void bnx2i_unreg_dev_all(void)
{
struct bnx2i_hba *hba, *temp;
- read_lock(&bnx2i_dev_lock);
+ mutex_lock(&bnx2i_dev_lock);
list_for_each_entry_safe(hba, temp, &adapter_list, link)
bnx2i_unreg_one_device(hba);
- read_unlock(&bnx2i_dev_lock);
+ mutex_unlock(&bnx2i_dev_lock);
}
@@ -267,35 +261,34 @@ void bnx2i_unreg_dev_all(void)
* @hba: bnx2i adapter instance
* @cnic: cnic device handle
*
- * Global resource lock and host adapter lock is held during critical sections
- * below. This routine is called from cnic_register_driver() context and
- * work horse thread which does majority of device specific initialization
+ * Global resource lock is held during critical sections below. This routine is
+ * called from either cnic_register_driver() or device hot plug context and
+ * and does majority of device specific initialization
*/
static int bnx2i_init_one(struct bnx2i_hba *hba, struct cnic_dev *cnic)
{
int rc;
- read_lock(&bnx2i_dev_lock);
- if (bnx2i_reg_device &&
- !test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
- rc = cnic->register_device(cnic, CNIC_ULP_ISCSI, hba);
- if (rc) /* duplicate registration */
- printk(KERN_ERR "bnx2i- dev reg failed\n");
-
- spin_lock(&hba->lock);
- bnx2i_reg_device++;
+ mutex_lock(&bnx2i_dev_lock);
+ rc = cnic->register_device(cnic, CNIC_ULP_ISCSI, hba);
+ if (!rc) {
hba->age++;
- spin_unlock(&hba->lock);
-
set_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
- }
- read_unlock(&bnx2i_dev_lock);
-
- write_lock(&bnx2i_dev_lock);
- list_add_tail(&hba->link, &adapter_list);
- adapter_count++;
- write_unlock(&bnx2i_dev_lock);
- return 0;
+ list_add_tail(&hba->link, &adapter_list);
+ adapter_count++;
+ } else if (rc == -EBUSY) /* duplicate registration */
+ printk(KERN_ALERT "bnx2i, duplicate registration"
+ "hba=%p, cnic=%p\n", hba, cnic);
+ else if (rc == -EAGAIN)
+ printk(KERN_ERR "bnx2i, driver not registered\n");
+ else if (rc == -EINVAL)
+ printk(KERN_ERR "bnx2i, invalid type %d\n", CNIC_ULP_ISCSI);
+ else
+ printk(KERN_ERR "bnx2i dev reg, unknown error, %d\n", rc);
+
+ mutex_unlock(&bnx2i_dev_lock);
+
+ return rc;
}
@@ -343,19 +336,15 @@ void bnx2i_ulp_exit(struct cnic_dev *dev)
"found, dev 0x%p\n", dev);
return;
}
- write_lock(&bnx2i_dev_lock);
+ mutex_lock(&bnx2i_dev_lock);
list_del_init(&hba->link);
adapter_count--;
if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI);
clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
-
- spin_lock(&hba->lock);
- bnx2i_reg_device--;
- spin_unlock(&hba->lock);
}
- write_unlock(&bnx2i_dev_lock);
+ mutex_unlock(&bnx2i_dev_lock);
bnx2i_free_hba(hba);
}
@@ -377,6 +366,8 @@ static int __init bnx2i_mod_init(void)
if (!is_power_of_2(sq_size))
sq_size = roundup_pow_of_two(sq_size);
+ mutex_init(&bnx2i_dev_lock);
+
bnx2i_scsi_xport_template =
iscsi_register_transport(&bnx2i_iscsi_transport);
if (!bnx2i_scsi_xport_template) {
@@ -412,7 +403,7 @@ static void __exit bnx2i_mod_exit(void)
{
struct bnx2i_hba *hba;
- write_lock(&bnx2i_dev_lock);
+ mutex_lock(&bnx2i_dev_lock);
while (!list_empty(&adapter_list)) {
hba = list_entry(adapter_list.next, struct bnx2i_hba, link);
list_del(&hba->link);
@@ -421,14 +412,11 @@ static void __exit bnx2i_mod_exit(void)
if (test_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic)) {
hba->cnic->unregister_device(hba->cnic, CNIC_ULP_ISCSI);
clear_bit(BNX2I_CNIC_REGISTERED, &hba->reg_with_cnic);
- bnx2i_reg_device--;
}
- write_unlock(&bnx2i_dev_lock);
bnx2i_free_hba(hba);
- write_lock(&bnx2i_dev_lock);
}
- write_unlock(&bnx2i_dev_lock);
+ mutex_unlock(&bnx2i_dev_lock);
iscsi_unregister_transport(&bnx2i_iscsi_transport);
cnic_unregister_driver(CNIC_ULP_ISCSI);
diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c
index f7412196f2f..9a7ba71f1af 100644
--- a/drivers/scsi/bnx2i/bnx2i_iscsi.c
+++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c
@@ -387,6 +387,7 @@ static struct iscsi_endpoint *bnx2i_alloc_ep(struct bnx2i_hba *hba)
bnx2i_ep = ep->dd_data;
INIT_LIST_HEAD(&bnx2i_ep->link);
bnx2i_ep->state = EP_STATE_IDLE;
+ bnx2i_ep->ep_iscsi_cid = (u16) -1;
bnx2i_ep->hba = hba;
bnx2i_ep->hba_age = hba->age;
hba->ofld_conns_active++;
@@ -1160,9 +1161,6 @@ static int bnx2i_task_xmit(struct iscsi_task *task)
struct bnx2i_cmd *cmd = task->dd_data;
struct iscsi_cmd *hdr = (struct iscsi_cmd *) task->hdr;
- if (test_bit(ADAPTER_STATE_LINK_DOWN, &hba->adapter_state))
- return -ENOTCONN;
-
if (!bnx2i_conn->is_bound)
return -ENOTCONN;
@@ -1653,15 +1651,18 @@ static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost,
struct iscsi_endpoint *ep;
int rc = 0;
- if (shost)
+ if (shost) {
/* driver is given scsi host to work with */
hba = iscsi_host_priv(shost);
- else
+ /* Register the device with cnic if not already done so */
+ bnx2i_register_device(hba);
+ } else
/*
* check if the given destination can be reached through
* a iscsi capable NetXtreme2 device
*/
hba = bnx2i_check_route(dst_addr);
+
if (!hba) {
rc = -ENOMEM;
goto check_busy;
@@ -1681,8 +1682,6 @@ static struct iscsi_endpoint *bnx2i_ep_connect(struct Scsi_Host *shost,
goto net_if_down;
}
- bnx2i_ep->state = EP_STATE_IDLE;
- bnx2i_ep->ep_iscsi_cid = (u16) -1;
bnx2i_ep->num_active_cmds = 0;
iscsi_cid = bnx2i_alloc_iscsi_cid(hba);
if (iscsi_cid == -1) {
diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c
index 7b1633a8c15..fe11c1d4b31 100644
--- a/drivers/scsi/ch.c
+++ b/drivers/scsi/ch.c
@@ -353,6 +353,12 @@ ch_readconfig(scsi_changer *ch)
/* look up the devices of the data transfer elements */
ch->dt = kmalloc(ch->counts[CHET_DT]*sizeof(struct scsi_device),
GFP_KERNEL);
+
+ if (!ch->dt) {
+ kfree(buffer);
+ return -ENOMEM;
+ }
+
for (elem = 0; elem < ch->counts[CHET_DT]; elem++) {
id = -1;
lun = 0;
diff --git a/drivers/scsi/constants.c b/drivers/scsi/constants.c
index e79e18101f8..63abb06c4ed 100644
--- a/drivers/scsi/constants.c
+++ b/drivers/scsi/constants.c
@@ -4,8 +4,7 @@
* Additions for SCSI 2 and Linux 2.2.x by D. Gilbert (990422)
* Additions for SCSI 3+ (SPC-3 T10/1416-D Rev 07 3 May 2002)
* by D. Gilbert and aeb (20020609)
- * Additions for SPC-3 T10/1416-D Rev 21 22 Sept 2004, D. Gilbert 20041025
- * Update to SPC-4 T10/1713-D Rev 5a, 14 June 2006, D. Gilbert 20060702
+ * Update to SPC-4 T10/1713-D Rev 20, 22 May 2009, D. Gilbert 20090624
*/
#include <linux/blkdev.h>
@@ -56,9 +55,9 @@ static const char * cdb_byte0_names[] = {
"Read Buffer",
/* 3d-3f */ "Update Block", "Read Long(10)", "Write Long(10)",
/* 40-41 */ "Change Definition", "Write Same(10)",
-/* 42-48 */ "Read sub-channel", "Read TOC/PMA/ATIP", "Read density support",
- "Play audio(10)", "Get configuration", "Play audio msf",
- "Play audio track/index",
+/* 42-48 */ "Unmap/Read sub-channel", "Read TOC/PMA/ATIP",
+ "Read density support", "Play audio(10)", "Get configuration",
+ "Play audio msf", "Play audio track/index",
/* 49-4f */ "Play track relative(10)", "Get event status notification",
"Pause/resume", "Log Select", "Log Sense", "Stop play/scan",
NULL,
@@ -71,12 +70,13 @@ static const char * cdb_byte0_names[] = {
/* 60-67 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 68-6f */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
/* 70-77 */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-/* 78-7f */ NULL, NULL, NULL, NULL, NULL, NULL, NULL, "Variable length",
+/* 78-7f */ NULL, NULL, NULL, NULL, NULL, NULL, "Extended CDB",
+ "Variable length",
/* 80-84 */ "Xdwrite(16)", "Rebuild(16)", "Regenerate(16)", "Extended copy",
"Receive copy results",
/* 85-89 */ "ATA command pass through(16)", "Access control in",
"Access control out", "Read(16)", "Memory Export Out(16)",
-/* 8a-8f */ "Write(16)", NULL, "Read attributes", "Write attributes",
+/* 8a-8f */ "Write(16)", "ORWrite", "Read attributes", "Write attributes",
"Write and verify(16)", "Verify(16)",
/* 90-94 */ "Pre-fetch(16)", "Synchronize cache(16)",
"Lock/unlock cache(16)", "Write same(16)", NULL,
@@ -107,22 +107,24 @@ struct value_name_pair {
};
static const struct value_name_pair maint_in_arr[] = {
- {0x5, "Report device identifier"},
+ {0x5, "Report identifying information"},
{0xa, "Report target port groups"},
{0xb, "Report aliases"},
{0xc, "Report supported operation codes"},
{0xd, "Report supported task management functions"},
{0xe, "Report priority"},
{0xf, "Report timestamp"},
+ {0x10, "Management protocol in"},
};
#define MAINT_IN_SZ ARRAY_SIZE(maint_in_arr)
static const struct value_name_pair maint_out_arr[] = {
- {0x6, "Set device identifier"},
+ {0x6, "Set identifying information"},
{0xa, "Set target port groups"},
{0xb, "Change aliases"},
{0xe, "Set priority"},
- {0xe, "Set timestamp"},
+ {0xf, "Set timestamp"},
+ {0x10, "Management protocol out"},
};
#define MAINT_OUT_SZ ARRAY_SIZE(maint_out_arr)
@@ -412,6 +414,7 @@ static const struct error_info additional[] =
{0x0004, "Beginning-of-partition/medium detected"},
{0x0005, "End-of-data detected"},
{0x0006, "I/O process terminated"},
+ {0x0007, "Programmable early warning detected"},
{0x0011, "Audio play operation in progress"},
{0x0012, "Audio play operation paused"},
{0x0013, "Audio play operation successfully completed"},
@@ -425,6 +428,7 @@ static const struct error_info additional[] =
{0x001B, "Set capacity operation in progress"},
{0x001C, "Verify operation in progress"},
{0x001D, "ATA pass through information available"},
+ {0x001E, "Conflicting SA creation request"},
{0x0100, "No index/sector signal"},
@@ -449,9 +453,12 @@ static const struct error_info additional[] =
{0x040B, "Logical unit not accessible, target port in standby state"},
{0x040C, "Logical unit not accessible, target port in unavailable "
"state"},
+ {0x040D, "Logical unit not ready, structure check required"},
{0x0410, "Logical unit not ready, auxiliary memory not accessible"},
{0x0411, "Logical unit not ready, notify (enable spinup) required"},
{0x0412, "Logical unit not ready, offline"},
+ {0x0413, "Logical unit not ready, SA creation in progress"},
+ {0x0414, "Logical unit not ready, space allocation in progress"},
{0x0500, "Logical unit does not respond to selection"},
@@ -479,6 +486,9 @@ static const struct error_info additional[] =
{0x0B03, "Warning - background self-test failed"},
{0x0B04, "Warning - background pre-scan detected medium error"},
{0x0B05, "Warning - background medium scan detected medium error"},
+ {0x0B06, "Warning - non-volatile cache now volatile"},
+ {0x0B07, "Warning - degraded power to non-volatile cache"},
+ {0x0B08, "Warning - power loss expected"},
{0x0C00, "Write error"},
{0x0C01, "Write error - recovered with auto reallocation"},
@@ -593,6 +603,7 @@ static const struct error_info additional[] =
{0x1C02, "Grown defect list not found"},
{0x1D00, "Miscompare during verify operation"},
+ {0x1D01, "Miscompare verify of unmapped LBA"},
{0x1E00, "Recovered id with ECC correction"},
@@ -626,6 +637,7 @@ static const struct error_info additional[] =
{0x2405, "Security working key frozen"},
{0x2406, "Nonce not unique"},
{0x2407, "Nonce timestamp out of range"},
+ {0x2408, "Invalid XCDB"},
{0x2500, "Logical unit not supported"},
@@ -656,10 +668,12 @@ static const struct error_info additional[] =
{0x2704, "Persistent write protect"},
{0x2705, "Permanent write protect"},
{0x2706, "Conditional write protect"},
+ {0x2707, "Space allocation failed write protect"},
{0x2800, "Not ready to ready change, medium may have changed"},
{0x2801, "Import or export element accessed"},
{0x2802, "Format-layer may have changed"},
+ {0x2803, "Import/export element accessed, medium changed"},
{0x2900, "Power on, reset, or bus device reset occurred"},
{0x2901, "Power on occurred"},
@@ -680,11 +694,16 @@ static const struct error_info additional[] =
{0x2A07, "Implicit asymmetric access state transition failed"},
{0x2A08, "Priority changed"},
{0x2A09, "Capacity data has changed"},
+ {0x2A0A, "Error history I_T nexus cleared"},
+ {0x2A0B, "Error history snapshot released"},
+ {0x2A0C, "Error recovery attributes have changed"},
+ {0x2A0D, "Data encryption capabilities changed"},
{0x2A10, "Timestamp changed"},
{0x2A11, "Data encryption parameters changed by another i_t nexus"},
{0x2A12, "Data encryption parameters changed by vendor specific "
"event"},
{0x2A13, "Data encryption key instance counter has changed"},
+ {0x2A14, "SA creation capabilities data has changed"},
{0x2B00, "Copy cannot execute since host cannot disconnect"},
@@ -723,6 +742,8 @@ static const struct error_info additional[] =
{0x300C, "WORM medium - overwrite attempted"},
{0x300D, "WORM medium - integrity check"},
{0x3010, "Medium not formatted"},
+ {0x3011, "Incompatible volume type"},
+ {0x3012, "Incompatible volume qualifier"},
{0x3100, "Medium format corrupted"},
{0x3101, "Format command failed"},
@@ -782,6 +803,10 @@ static const struct error_info additional[] =
{0x3B15, "Medium magazine unlocked"},
{0x3B16, "Mechanical positioning or changer error"},
{0x3B17, "Read past end of user object"},
+ {0x3B18, "Element disabled"},
+ {0x3B19, "Element enabled"},
+ {0x3B1A, "Data transfer device removed"},
+ {0x3B1B, "Data transfer device inserted"},
{0x3D00, "Invalid bits in identify message"},
@@ -882,6 +907,8 @@ static const struct error_info additional[] =
{0x5506, "Auxiliary memory out of space"},
{0x5507, "Quota error"},
{0x5508, "Maximum number of supplemental decryption keys exceeded"},
+ {0x5509, "Medium auxiliary memory not accessible"},
+ {0x550A, "Data currently unavailable"},
{0x5700, "Unable to recover table-of-contents"},
@@ -993,6 +1020,12 @@ static const struct error_info additional[] =
{0x5E02, "Standby condition activated by timer"},
{0x5E03, "Idle condition activated by command"},
{0x5E04, "Standby condition activated by command"},
+ {0x5E05, "Idle_b condition activated by timer"},
+ {0x5E06, "Idle_b condition activated by command"},
+ {0x5E07, "Idle_c condition activated by timer"},
+ {0x5E08, "Idle_c condition activated by command"},
+ {0x5E09, "Standby_y condition activated by timer"},
+ {0x5E0A, "Standby_y condition activated by command"},
{0x5E41, "Power state change to active"},
{0x5E42, "Power state change to idle"},
{0x5E43, "Power state change to standby"},
@@ -1091,7 +1124,28 @@ static const struct error_info additional[] =
{0x7403, "Incorrect data encryption key"},
{0x7404, "Cryptographic integrity validation failed"},
{0x7405, "Error decrypting data"},
+ {0x7406, "Unknown signature verification key"},
+ {0x7407, "Encryption parameters not useable"},
+ {0x7408, "Digital signature validation failure"},
+ {0x7409, "Encryption mode mismatch on read"},
+ {0x740A, "Encrypted block not raw read enabled"},
+ {0x740B, "Incorrect Encryption parameters"},
+ {0x740C, "Unable to decrypt parameter list"},
+ {0x740D, "Encryption algorithm disabled"},
+ {0x7410, "SA creation parameter value invalid"},
+ {0x7411, "SA creation parameter value rejected"},
+ {0x7412, "Invalid SA usage"},
+ {0x7421, "Data Encryption configuration prevented"},
+ {0x7430, "SA creation parameter not supported"},
+ {0x7440, "Authentication failed"},
+ {0x7461, "External data encryption key manager access error"},
+ {0x7462, "External data encryption key manager error"},
+ {0x7463, "External data encryption key not found"},
+ {0x7464, "External data encryption request not authorized"},
+ {0x746E, "External data encryption control timeout"},
+ {0x746F, "External data encryption control error"},
{0x7471, "Logical unit access not authorized"},
+ {0x7479, "Security conflict in translated device"},
{0, NULL}
};
@@ -1103,12 +1157,12 @@ struct error_info2 {
static const struct error_info2 additional2[] =
{
- {0x40,0x00,0x7f,"Ram failure (%x)"},
- {0x40,0x80,0xff,"Diagnostic failure on component (%x)"},
- {0x41,0x00,0xff,"Data path failure (%x)"},
- {0x42,0x00,0xff,"Power-on or self-test failure (%x)"},
- {0x4D,0x00,0xff,"Tagged overlapped commands (queue tag %x)"},
- {0x70,0x00,0xff,"Decompression exception short algorithm id of %x"},
+ {0x40, 0x00, 0x7f, "Ram failure (%x)"},
+ {0x40, 0x80, 0xff, "Diagnostic failure on component (%x)"},
+ {0x41, 0x00, 0xff, "Data path failure (%x)"},
+ {0x42, 0x00, 0xff, "Power-on or self-test failure (%x)"},
+ {0x4D, 0x00, 0xff, "Tagged overlapped commands (task tag %x)"},
+ {0x70, 0x00, 0xff, "Decompression exception short algorithm id of %x"},
{0, 0, 0, NULL}
};
@@ -1157,14 +1211,15 @@ scsi_extd_sense_format(unsigned char asc, unsigned char ascq) {
int i;
unsigned short code = ((asc << 8) | ascq);
- for (i=0; additional[i].text; i++)
+ for (i = 0; additional[i].text; i++)
if (additional[i].code12 == code)
return additional[i].text;
- for (i=0; additional2[i].fmt; i++)
+ for (i = 0; additional2[i].fmt; i++) {
if (additional2[i].code1 == asc &&
- additional2[i].code2_min >= ascq &&
- additional2[i].code2_max <= ascq)
+ ascq >= additional2[i].code2_min &&
+ ascq <= additional2[i].code2_max)
return additional2[i].fmt;
+ }
#endif
return NULL;
}
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c
index a518f2eff19..3ee1cbc8947 100644
--- a/drivers/scsi/device_handler/scsi_dh.c
+++ b/drivers/scsi/device_handler/scsi_dh.c
@@ -153,12 +153,24 @@ static int scsi_dh_handler_attach(struct scsi_device *sdev,
if (sdev->scsi_dh_data) {
if (sdev->scsi_dh_data->scsi_dh != scsi_dh)
err = -EBUSY;
- } else if (scsi_dh->attach)
+ else
+ kref_get(&sdev->scsi_dh_data->kref);
+ } else if (scsi_dh->attach) {
err = scsi_dh->attach(sdev);
-
+ if (!err) {
+ kref_init(&sdev->scsi_dh_data->kref);
+ sdev->scsi_dh_data->sdev = sdev;
+ }
+ }
return err;
}
+static void __detach_handler (struct kref *kref)
+{
+ struct scsi_dh_data *scsi_dh_data = container_of(kref, struct scsi_dh_data, kref);
+ scsi_dh_data->scsi_dh->detach(scsi_dh_data->sdev);
+}
+
/*
* scsi_dh_handler_detach - Detach a device handler from a device
* @sdev - SCSI device the device handler should be detached from
@@ -180,7 +192,7 @@ static void scsi_dh_handler_detach(struct scsi_device *sdev,
scsi_dh = sdev->scsi_dh_data->scsi_dh;
if (scsi_dh && scsi_dh->detach)
- scsi_dh->detach(sdev);
+ kref_put(&sdev->scsi_dh_data->kref, __detach_handler);
}
/*
@@ -440,6 +452,39 @@ int scsi_dh_activate(struct request_queue *q)
EXPORT_SYMBOL_GPL(scsi_dh_activate);
/*
+ * scsi_dh_set_params - set the parameters for the device as per the
+ * string specified in params.
+ * @q - Request queue that is associated with the scsi_device for
+ * which the parameters to be set.
+ * @params - parameters in the following format
+ * "no_of_params\0param1\0param2\0param3\0...\0"
+ * for example, string for 2 parameters with value 10 and 21
+ * is specified as "2\010\021\0".
+ */
+int scsi_dh_set_params(struct request_queue *q, const char *params)
+{
+ int err = -SCSI_DH_NOSYS;
+ unsigned long flags;
+ struct scsi_device *sdev;
+ struct scsi_device_handler *scsi_dh = NULL;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ sdev = q->queuedata;
+ if (sdev && sdev->scsi_dh_data)
+ scsi_dh = sdev->scsi_dh_data->scsi_dh;
+ if (scsi_dh && scsi_dh->set_params && get_device(&sdev->sdev_gendev))
+ err = 0;
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ if (err)
+ return err;
+ err = scsi_dh->set_params(sdev, params);
+ put_device(&sdev->sdev_gendev);
+ return err;
+}
+EXPORT_SYMBOL_GPL(scsi_dh_set_params);
+
+/*
* scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for
* the given name. FALSE(0) otherwise.
* @name - name of the device handler.
@@ -474,7 +519,6 @@ int scsi_dh_attach(struct request_queue *q, const char *name)
if (!err) {
err = scsi_dh_handler_attach(sdev, scsi_dh);
-
put_device(&sdev->sdev_gendev);
}
return err;
@@ -505,10 +549,8 @@ void scsi_dh_detach(struct request_queue *q)
return;
if (sdev->scsi_dh_data) {
- /* if sdev is not on internal list, detach */
scsi_dh = sdev->scsi_dh_data->scsi_dh;
- if (!device_handler_match(scsi_dh, sdev))
- scsi_dh_handler_detach(sdev, scsi_dh);
+ scsi_dh_handler_detach(sdev, scsi_dh);
}
put_device(&sdev->sdev_gendev);
}
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index dba154c8ff6..b5cdefaf260 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -663,7 +663,7 @@ static int alua_activate(struct scsi_device *sdev)
goto out;
}
- if (h->tpgs == TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED)
+ if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED)
err = alua_stpg(sdev, TPGS_STATE_OPTIMIZED, h);
out:
diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c
index 0e572d2c5b0..0cffe84976f 100644
--- a/drivers/scsi/device_handler/scsi_dh_emc.c
+++ b/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -561,6 +561,61 @@ done:
return result;
}
+/*
+ * params - parameters in the following format
+ * "no_of_params\0param1\0param2\0param3\0...\0"
+ * for example, string for 2 parameters with value 10 and 21
+ * is specified as "2\010\021\0".
+ */
+static int clariion_set_params(struct scsi_device *sdev, const char *params)
+{
+ struct clariion_dh_data *csdev = get_clariion_data(sdev);
+ unsigned int hr = 0, st = 0, argc;
+ const char *p = params;
+ int result = SCSI_DH_OK;
+
+ if ((sscanf(params, "%u", &argc) != 1) || (argc != 2))
+ return -EINVAL;
+
+ while (*p++)
+ ;
+ if ((sscanf(p, "%u", &st) != 1) || (st > 1))
+ return -EINVAL;
+
+ while (*p++)
+ ;
+ if ((sscanf(p, "%u", &hr) != 1) || (hr > 1))
+ return -EINVAL;
+
+ if (st)
+ csdev->flags |= CLARIION_SHORT_TRESPASS;
+ else
+ csdev->flags &= ~CLARIION_SHORT_TRESPASS;
+
+ if (hr)
+ csdev->flags |= CLARIION_HONOR_RESERVATIONS;
+ else
+ csdev->flags &= ~CLARIION_HONOR_RESERVATIONS;
+
+ /*
+ * If this path is owned, we have to send a trespass command
+ * with the new parameters. If not, simply return. Next trespass
+ * command would use the parameters.
+ */
+ if (csdev->lun_state != CLARIION_LUN_OWNED)
+ goto done;
+
+ csdev->lun_state = CLARIION_LUN_UNINITIALIZED;
+ result = send_trespass_cmd(sdev, csdev);
+ if (result != SCSI_DH_OK)
+ goto done;
+
+ /* Update status */
+ result = clariion_send_inquiry(sdev, csdev);
+
+done:
+ return result;
+}
static const struct scsi_dh_devlist clariion_dev_list[] = {
{"DGC", "RAID"},
@@ -581,11 +636,9 @@ static struct scsi_device_handler clariion_dh = {
.check_sense = clariion_check_sense,
.activate = clariion_activate,
.prep_fn = clariion_prep_fn,
+ .set_params = clariion_set_params,
};
-/*
- * TODO: need some interface so we can set trespass values
- */
static int clariion_bus_attach(struct scsi_device *sdev)
{
struct scsi_dh_data *scsi_dh_data;
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
index fd0544f7da8..11c89311427 100644
--- a/drivers/scsi/device_handler/scsi_dh_rdac.c
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -112,6 +112,7 @@ struct c9_inquiry {
#define SUBSYS_ID_LEN 16
#define SLOT_ID_LEN 2
+#define ARRAY_LABEL_LEN 31
struct c4_inquiry {
u8 peripheral_info;
@@ -135,6 +136,8 @@ struct rdac_controller {
struct rdac_pg_legacy legacy;
struct rdac_pg_expanded expanded;
} mode_select;
+ u8 index;
+ u8 array_name[ARRAY_LABEL_LEN];
};
struct c8_inquiry {
u8 peripheral_info;
@@ -198,6 +201,31 @@ static const char *lun_state[] =
static LIST_HEAD(ctlr_list);
static DEFINE_SPINLOCK(list_lock);
+/*
+ * module parameter to enable rdac debug logging.
+ * 2 bits for each type of logging, only two types defined for now
+ * Can be enhanced if required at later point
+ */
+static int rdac_logging = 1;
+module_param(rdac_logging, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(rdac_logging, "A bit mask of rdac logging levels, "
+ "Default is 1 - failover logging enabled, "
+ "set it to 0xF to enable all the logs");
+
+#define RDAC_LOG_FAILOVER 0
+#define RDAC_LOG_SENSE 2
+
+#define RDAC_LOG_BITS 2
+
+#define RDAC_LOG_LEVEL(SHIFT) \
+ ((rdac_logging >> (SHIFT)) & ((1 << (RDAC_LOG_BITS)) - 1))
+
+#define RDAC_LOG(SHIFT, sdev, f, arg...) \
+do { \
+ if (unlikely(RDAC_LOG_LEVEL(SHIFT))) \
+ sdev_printk(KERN_INFO, sdev, RDAC_NAME ": " f "\n", ## arg); \
+} while (0);
+
static inline struct rdac_dh_data *get_rdac_data(struct scsi_device *sdev)
{
struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
@@ -303,7 +331,8 @@ static void release_controller(struct kref *kref)
kfree(ctlr);
}
-static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id)
+static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id,
+ char *array_name)
{
struct rdac_controller *ctlr, *tmp;
@@ -324,6 +353,14 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id)
/* initialize fields of controller */
memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
+ memcpy(ctlr->array_name, array_name, ARRAY_LABEL_LEN);
+
+ /* update the controller index */
+ if (slot_id[1] == 0x31)
+ ctlr->index = 0;
+ else
+ ctlr->index = 1;
+
kref_init(&ctlr->kref);
ctlr->use_ms10 = -1;
list_add(&ctlr->node, &ctlr_list);
@@ -363,9 +400,10 @@ done:
return err;
}
-static int get_lun(struct scsi_device *sdev, struct rdac_dh_data *h)
+static int get_lun_info(struct scsi_device *sdev, struct rdac_dh_data *h,
+ char *array_name)
{
- int err;
+ int err, i;
struct c8_inquiry *inqp;
err = submit_inquiry(sdev, 0xC8, sizeof(struct c8_inquiry), h);
@@ -377,6 +415,11 @@ static int get_lun(struct scsi_device *sdev, struct rdac_dh_data *h)
inqp->page_id[2] != 'i' || inqp->page_id[3] != 'd')
return SCSI_DH_NOSYS;
h->lun = inqp->lun[7]; /* Uses only the last byte */
+
+ for(i=0; i<ARRAY_LABEL_LEN-1; ++i)
+ *(array_name+i) = inqp->array_user_label[(2*i)+1];
+
+ *(array_name+ARRAY_LABEL_LEN-1) = '\0';
}
return err;
}
@@ -410,7 +453,7 @@ static int check_ownership(struct scsi_device *sdev, struct rdac_dh_data *h)
}
static int initialize_controller(struct scsi_device *sdev,
- struct rdac_dh_data *h)
+ struct rdac_dh_data *h, char *array_name)
{
int err;
struct c4_inquiry *inqp;
@@ -418,7 +461,8 @@ static int initialize_controller(struct scsi_device *sdev,
err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry), h);
if (err == SCSI_DH_OK) {
inqp = &h->inq.c4;
- h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id);
+ h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id,
+ array_name);
if (!h->ctlr)
err = SCSI_DH_RES_TEMP_UNAVAIL;
}
@@ -450,6 +494,7 @@ static int mode_select_handle_sense(struct scsi_device *sdev,
{
struct scsi_sense_hdr sense_hdr;
int err = SCSI_DH_IO, ret;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
ret = scsi_normalize_sense(sensebuf, SCSI_SENSE_BUFFERSIZE, &sense_hdr);
if (!ret)
@@ -478,11 +523,14 @@ static int mode_select_handle_sense(struct scsi_device *sdev,
err = SCSI_DH_RETRY;
break;
default:
- sdev_printk(KERN_INFO, sdev,
- "MODE_SELECT failed with sense %02x/%02x/%02x.\n",
- sense_hdr.sense_key, sense_hdr.asc, sense_hdr.ascq);
+ break;
}
+ RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
+ "MODE_SELECT returned with sense %02x/%02x/%02x",
+ (char *) h->ctlr->array_name, h->ctlr->index,
+ sense_hdr.sense_key, sense_hdr.asc, sense_hdr.ascq);
+
done:
return err;
}
@@ -499,7 +547,9 @@ retry:
if (!rq)
goto done;
- sdev_printk(KERN_INFO, sdev, "%s MODE_SELECT command.\n",
+ RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
+ "%s MODE_SELECT command",
+ (char *) h->ctlr->array_name, h->ctlr->index,
(retry_cnt == RDAC_RETRY_COUNT) ? "queueing" : "retrying");
err = blk_execute_rq(q, NULL, rq, 1);
@@ -509,8 +559,12 @@ retry:
if (err == SCSI_DH_RETRY && retry_cnt--)
goto retry;
}
- if (err == SCSI_DH_OK)
+ if (err == SCSI_DH_OK) {
h->state = RDAC_STATE_ACTIVE;
+ RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "
+ "MODE_SELECT completed",
+ (char *) h->ctlr->array_name, h->ctlr->index);
+ }
done:
return err;
@@ -525,17 +579,6 @@ static int rdac_activate(struct scsi_device *sdev)
if (err != SCSI_DH_OK)
goto done;
- if (!h->ctlr) {
- err = initialize_controller(sdev, h);
- if (err != SCSI_DH_OK)
- goto done;
- }
-
- if (h->ctlr->use_ms10 == -1) {
- err = set_mode_select(sdev, h);
- if (err != SCSI_DH_OK)
- goto done;
- }
if (h->lun_state == RDAC_LUN_UNOWNED)
err = send_mode_select(sdev, h);
done:
@@ -559,6 +602,12 @@ static int rdac_check_sense(struct scsi_device *sdev,
struct scsi_sense_hdr *sense_hdr)
{
struct rdac_dh_data *h = get_rdac_data(sdev);
+
+ RDAC_LOG(RDAC_LOG_SENSE, sdev, "array %s, ctlr %d, "
+ "I/O returned with sense %02x/%02x/%02x",
+ (char *) h->ctlr->array_name, h->ctlr->index,
+ sense_hdr->sense_key, sense_hdr->asc, sense_hdr->ascq);
+
switch (sense_hdr->sense_key) {
case NOT_READY:
if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x01)
@@ -628,11 +677,18 @@ static const struct scsi_dh_devlist rdac_dev_list[] = {
{"SGI", "IS"},
{"STK", "OPENstorage D280"},
{"SUN", "CSM200_R"},
+ {"SUN", "LCSM100_I"},
+ {"SUN", "LCSM100_S"},
+ {"SUN", "LCSM100_E"},
{"SUN", "LCSM100_F"},
{"DELL", "MD3000"},
{"DELL", "MD3000i"},
+ {"DELL", "MD32xx"},
+ {"DELL", "MD32xxi"},
{"LSI", "INF-01-00"},
{"ENGENIO", "INF-01-00"},
+ {"STK", "FLEXLINE 380"},
+ {"SUN", "CSM100_R_FC"},
{NULL, NULL},
};
@@ -656,6 +712,7 @@ static int rdac_bus_attach(struct scsi_device *sdev)
struct rdac_dh_data *h;
unsigned long flags;
int err;
+ char array_name[ARRAY_LABEL_LEN];
scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+ sizeof(*h) , GFP_KERNEL);
@@ -670,16 +727,24 @@ static int rdac_bus_attach(struct scsi_device *sdev)
h->lun = UNINITIALIZED_LUN;
h->state = RDAC_STATE_ACTIVE;
- err = get_lun(sdev, h);
+ err = get_lun_info(sdev, h, array_name);
if (err != SCSI_DH_OK)
goto failed;
- err = check_ownership(sdev, h);
+ err = initialize_controller(sdev, h, array_name);
if (err != SCSI_DH_OK)
goto failed;
+ err = check_ownership(sdev, h);
+ if (err != SCSI_DH_OK)
+ goto clean_ctlr;
+
+ err = set_mode_select(sdev, h);
+ if (err != SCSI_DH_OK)
+ goto clean_ctlr;
+
if (!try_module_get(THIS_MODULE))
- goto failed;
+ goto clean_ctlr;
spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
sdev->scsi_dh_data = scsi_dh_data;
@@ -691,6 +756,9 @@ static int rdac_bus_attach(struct scsi_device *sdev)
return 0;
+clean_ctlr:
+ kref_put(&h->ctlr->kref, release_controller);
+
failed:
kfree(scsi_dh_data);
sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c
index 0a5609bb581..704b8e03494 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -1,5 +1,5 @@
/*
- * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved.
+ * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
@@ -49,9 +49,20 @@ MODULE_AUTHOR("Open-FCoE.org");
MODULE_DESCRIPTION("FCoE");
MODULE_LICENSE("GPL v2");
+/* Performance tuning parameters for fcoe */
+static unsigned int fcoe_ddp_min;
+module_param_named(ddp_min, fcoe_ddp_min, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ddp_min, "Minimum I/O size in bytes for " \
+ "Direct Data Placement (DDP).");
+
+DEFINE_MUTEX(fcoe_config_mutex);
+
+/* fcoe_percpu_clean completion. Waiter protected by fcoe_create_mutex */
+static DECLARE_COMPLETION(fcoe_flush_completion);
+
/* fcoe host list */
+/* must only by accessed under the RTNL mutex */
LIST_HEAD(fcoe_hostlist);
-DEFINE_RWLOCK(fcoe_hostlist_lock);
DEFINE_PER_CPU(struct fcoe_percpu_s, fcoe_percpu);
/* Function Prototypes */
@@ -66,12 +77,13 @@ static int fcoe_link_ok(struct fc_lport *lp);
static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *);
static int fcoe_hostlist_add(const struct fc_lport *);
-static int fcoe_hostlist_remove(const struct fc_lport *);
static void fcoe_check_wait_queue(struct fc_lport *, struct sk_buff *);
static int fcoe_device_notification(struct notifier_block *, ulong, void *);
static void fcoe_dev_setup(void);
static void fcoe_dev_cleanup(void);
+static struct fcoe_interface *
+ fcoe_hostlist_lookup_port(const struct net_device *dev);
/* notification function from net device */
static struct notifier_block fcoe_notifier = {
@@ -132,6 +144,180 @@ static struct scsi_host_template fcoe_shost_template = {
.max_sectors = 0xffff,
};
+static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
+ struct packet_type *ptype,
+ struct net_device *orig_dev);
+/**
+ * fcoe_interface_setup()
+ * @fcoe: new fcoe_interface
+ * @netdev : ptr to the associated netdevice struct
+ *
+ * Returns : 0 for success
+ * Locking: must be called with the RTNL mutex held
+ */
+static int fcoe_interface_setup(struct fcoe_interface *fcoe,
+ struct net_device *netdev)
+{
+ struct fcoe_ctlr *fip = &fcoe->ctlr;
+ struct netdev_hw_addr *ha;
+ u8 flogi_maddr[ETH_ALEN];
+
+ fcoe->netdev = netdev;
+
+ /* Do not support for bonding device */
+ if ((netdev->priv_flags & IFF_MASTER_ALB) ||
+ (netdev->priv_flags & IFF_SLAVE_INACTIVE) ||
+ (netdev->priv_flags & IFF_MASTER_8023AD)) {
+ return -EOPNOTSUPP;
+ }
+
+ /* look for SAN MAC address, if multiple SAN MACs exist, only
+ * use the first one for SPMA */
+ rcu_read_lock();
+ for_each_dev_addr(netdev, ha) {
+ if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
+ (is_valid_ether_addr(fip->ctl_src_addr))) {
+ memcpy(fip->ctl_src_addr, ha->addr, ETH_ALEN);
+ fip->spma = 1;
+ break;
+ }
+ }
+ rcu_read_unlock();
+
+ /* setup Source Mac Address */
+ if (!fip->spma)
+ memcpy(fip->ctl_src_addr, netdev->dev_addr, netdev->addr_len);
+
+ /*
+ * Add FCoE MAC address as second unicast MAC address
+ * or enter promiscuous mode if not capable of listening
+ * for multiple unicast MACs.
+ */
+ memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+ dev_unicast_add(netdev, flogi_maddr);
+ if (fip->spma)
+ dev_unicast_add(netdev, fip->ctl_src_addr);
+ dev_mc_add(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
+
+ /*
+ * setup the receive function from ethernet driver
+ * on the ethertype for the given device
+ */
+ fcoe->fcoe_packet_type.func = fcoe_rcv;
+ fcoe->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
+ fcoe->fcoe_packet_type.dev = netdev;
+ dev_add_pack(&fcoe->fcoe_packet_type);
+
+ fcoe->fip_packet_type.func = fcoe_fip_recv;
+ fcoe->fip_packet_type.type = htons(ETH_P_FIP);
+ fcoe->fip_packet_type.dev = netdev;
+ dev_add_pack(&fcoe->fip_packet_type);
+
+ return 0;
+}
+
+static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb);
+static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new);
+static void fcoe_destroy_work(struct work_struct *work);
+
+/**
+ * fcoe_interface_create()
+ * @netdev: network interface
+ *
+ * Returns: pointer to a struct fcoe_interface or NULL on error
+ */
+static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev)
+{
+ struct fcoe_interface *fcoe;
+
+ fcoe = kzalloc(sizeof(*fcoe), GFP_KERNEL);
+ if (!fcoe) {
+ FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n");
+ return NULL;
+ }
+
+ dev_hold(netdev);
+ kref_init(&fcoe->kref);
+
+ /*
+ * Initialize FIP.
+ */
+ fcoe_ctlr_init(&fcoe->ctlr);
+ fcoe->ctlr.send = fcoe_fip_send;
+ fcoe->ctlr.update_mac = fcoe_update_src_mac;
+
+ fcoe_interface_setup(fcoe, netdev);
+
+ return fcoe;
+}
+
+/**
+ * fcoe_interface_cleanup() - clean up netdev configurations
+ * @fcoe:
+ *
+ * Caller must be holding the RTNL mutex
+ */
+void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
+{
+ struct net_device *netdev = fcoe->netdev;
+ struct fcoe_ctlr *fip = &fcoe->ctlr;
+ u8 flogi_maddr[ETH_ALEN];
+
+ /*
+ * Don't listen for Ethernet packets anymore.
+ * synchronize_net() ensures that the packet handlers are not running
+ * on another CPU. dev_remove_pack() would do that, this calls the
+ * unsyncronized version __dev_remove_pack() to avoid multiple delays.
+ */
+ __dev_remove_pack(&fcoe->fcoe_packet_type);
+ __dev_remove_pack(&fcoe->fip_packet_type);
+ synchronize_net();
+
+ /* Delete secondary MAC addresses */
+ memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
+ dev_unicast_delete(netdev, flogi_maddr);
+ if (!is_zero_ether_addr(fip->data_src_addr))
+ dev_unicast_delete(netdev, fip->data_src_addr);
+ if (fip->spma)
+ dev_unicast_delete(netdev, fip->ctl_src_addr);
+ dev_mc_delete(netdev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
+}
+
+/**
+ * fcoe_interface_release() - fcoe_port kref release function
+ * @kref: embedded reference count in an fcoe_interface struct
+ */
+static void fcoe_interface_release(struct kref *kref)
+{
+ struct fcoe_interface *fcoe;
+ struct net_device *netdev;
+
+ fcoe = container_of(kref, struct fcoe_interface, kref);
+ netdev = fcoe->netdev;
+ /* tear-down the FCoE controller */
+ fcoe_ctlr_destroy(&fcoe->ctlr);
+ kfree(fcoe);
+ dev_put(netdev);
+}
+
+/**
+ * fcoe_interface_get()
+ * @fcoe:
+ */
+static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
+{
+ kref_get(&fcoe->kref);
+}
+
+/**
+ * fcoe_interface_put()
+ * @fcoe:
+ */
+static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
+{
+ kref_put(&fcoe->kref, fcoe_interface_release);
+}
+
/**
* fcoe_fip_recv - handle a received FIP frame.
* @skb: the receive skb
@@ -145,10 +331,10 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype,
struct net_device *orig_dev)
{
- struct fcoe_softc *fc;
+ struct fcoe_interface *fcoe;
- fc = container_of(ptype, struct fcoe_softc, fip_packet_type);
- fcoe_ctlr_recv(&fc->ctlr, skb);
+ fcoe = container_of(ptype, struct fcoe_interface, fip_packet_type);
+ fcoe_ctlr_recv(&fcoe->ctlr, skb);
return 0;
}
@@ -159,7 +345,7 @@ static int fcoe_fip_recv(struct sk_buff *skb, struct net_device *dev,
*/
static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
{
- skb->dev = fcoe_from_ctlr(fip)->real_dev;
+ skb->dev = fcoe_from_ctlr(fip)->netdev;
dev_queue_xmit(skb);
}
@@ -174,13 +360,13 @@ static void fcoe_fip_send(struct fcoe_ctlr *fip, struct sk_buff *skb)
*/
static void fcoe_update_src_mac(struct fcoe_ctlr *fip, u8 *old, u8 *new)
{
- struct fcoe_softc *fc;
+ struct fcoe_interface *fcoe;
- fc = fcoe_from_ctlr(fip);
+ fcoe = fcoe_from_ctlr(fip);
rtnl_lock();
if (!is_zero_ether_addr(old))
- dev_unicast_delete(fc->real_dev, old);
- dev_unicast_add(fc->real_dev, new);
+ dev_unicast_delete(fcoe->netdev, old);
+ dev_unicast_add(fcoe->netdev, new);
rtnl_unlock();
}
@@ -217,30 +403,6 @@ static int fcoe_lport_config(struct fc_lport *lp)
}
/**
- * fcoe_netdev_cleanup() - clean up netdev configurations
- * @fc: ptr to the fcoe_softc
- */
-void fcoe_netdev_cleanup(struct fcoe_softc *fc)
-{
- u8 flogi_maddr[ETH_ALEN];
-
- /* Don't listen for Ethernet packets anymore */
- dev_remove_pack(&fc->fcoe_packet_type);
- dev_remove_pack(&fc->fip_packet_type);
-
- /* Delete secondary MAC addresses */
- rtnl_lock();
- memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
- dev_unicast_delete(fc->real_dev, flogi_maddr);
- if (!is_zero_ether_addr(fc->ctlr.data_src_addr))
- dev_unicast_delete(fc->real_dev, fc->ctlr.data_src_addr);
- if (fc->ctlr.spma)
- dev_unicast_delete(fc->real_dev, fc->ctlr.ctl_src_addr);
- dev_mc_delete(fc->real_dev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
- rtnl_unlock();
-}
-
-/**
* fcoe_queue_timer() - fcoe queue timer
* @lp: the fc_lport pointer
*
@@ -265,116 +427,53 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev)
{
u32 mfs;
u64 wwnn, wwpn;
- struct fcoe_softc *fc;
- u8 flogi_maddr[ETH_ALEN];
- struct netdev_hw_addr *ha;
+ struct fcoe_interface *fcoe;
+ struct fcoe_port *port;
/* Setup lport private data to point to fcoe softc */
- fc = lport_priv(lp);
- fc->ctlr.lp = lp;
- fc->real_dev = netdev;
- fc->phys_dev = netdev;
-
- /* Require support for get_pauseparam ethtool op. */
- if (netdev->priv_flags & IFF_802_1Q_VLAN)
- fc->phys_dev = vlan_dev_real_dev(netdev);
-
- /* Do not support for bonding device */
- if ((fc->real_dev->priv_flags & IFF_MASTER_ALB) ||
- (fc->real_dev->priv_flags & IFF_SLAVE_INACTIVE) ||
- (fc->real_dev->priv_flags & IFF_MASTER_8023AD)) {
- return -EOPNOTSUPP;
- }
+ port = lport_priv(lp);
+ fcoe = port->fcoe;
/*
* Determine max frame size based on underlying device and optional
* user-configured limit. If the MFS is too low, fcoe_link_ok()
* will return 0, so do this first.
*/
- mfs = fc->real_dev->mtu - (sizeof(struct fcoe_hdr) +
- sizeof(struct fcoe_crc_eof));
+ mfs = netdev->mtu - (sizeof(struct fcoe_hdr) +
+ sizeof(struct fcoe_crc_eof));
if (fc_set_mfs(lp, mfs))
return -EINVAL;
/* offload features support */
- if (fc->real_dev->features & NETIF_F_SG)
+ if (netdev->features & NETIF_F_SG)
lp->sg_supp = 1;
-#ifdef NETIF_F_FCOE_CRC
if (netdev->features & NETIF_F_FCOE_CRC) {
lp->crc_offload = 1;
FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n");
}
-#endif
-#ifdef NETIF_F_FSO
if (netdev->features & NETIF_F_FSO) {
lp->seq_offload = 1;
lp->lso_max = netdev->gso_max_size;
FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n",
lp->lso_max);
}
-#endif
if (netdev->fcoe_ddp_xid) {
lp->lro_enabled = 1;
lp->lro_xid = netdev->fcoe_ddp_xid;
FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n",
lp->lro_xid);
}
- skb_queue_head_init(&fc->fcoe_pending_queue);
- fc->fcoe_pending_queue_active = 0;
- setup_timer(&fc->timer, fcoe_queue_timer, (unsigned long)lp);
-
- /* look for SAN MAC address, if multiple SAN MACs exist, only
- * use the first one for SPMA */
- rcu_read_lock();
- for_each_dev_addr(netdev, ha) {
- if ((ha->type == NETDEV_HW_ADDR_T_SAN) &&
- (is_valid_ether_addr(fc->ctlr.ctl_src_addr))) {
- memcpy(fc->ctlr.ctl_src_addr, ha->addr, ETH_ALEN);
- fc->ctlr.spma = 1;
- break;
- }
- }
- rcu_read_unlock();
-
- /* setup Source Mac Address */
- if (!fc->ctlr.spma)
- memcpy(fc->ctlr.ctl_src_addr, fc->real_dev->dev_addr,
- fc->real_dev->addr_len);
+ skb_queue_head_init(&port->fcoe_pending_queue);
+ port->fcoe_pending_queue_active = 0;
+ setup_timer(&port->timer, fcoe_queue_timer, (unsigned long)lp);
- wwnn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 1, 0);
+ wwnn = fcoe_wwn_from_mac(netdev->dev_addr, 1, 0);
fc_set_wwnn(lp, wwnn);
/* XXX - 3rd arg needs to be vlan id */
- wwpn = fcoe_wwn_from_mac(fc->real_dev->dev_addr, 2, 0);
+ wwpn = fcoe_wwn_from_mac(netdev->dev_addr, 2, 0);
fc_set_wwpn(lp, wwpn);
- /*
- * Add FCoE MAC address as second unicast MAC address
- * or enter promiscuous mode if not capable of listening
- * for multiple unicast MACs.
- */
- rtnl_lock();
- memcpy(flogi_maddr, (u8[6]) FC_FCOE_FLOGI_MAC, ETH_ALEN);
- dev_unicast_add(fc->real_dev, flogi_maddr);
- if (fc->ctlr.spma)
- dev_unicast_add(fc->real_dev, fc->ctlr.ctl_src_addr);
- dev_mc_add(fc->real_dev, FIP_ALL_ENODE_MACS, ETH_ALEN, 0);
- rtnl_unlock();
-
- /*
- * setup the receive function from ethernet driver
- * on the ethertype for the given device
- */
- fc->fcoe_packet_type.func = fcoe_rcv;
- fc->fcoe_packet_type.type = __constant_htons(ETH_P_FCOE);
- fc->fcoe_packet_type.dev = fc->real_dev;
- dev_add_pack(&fc->fcoe_packet_type);
-
- fc->fip_packet_type.func = fcoe_fip_recv;
- fc->fip_packet_type.type = htons(ETH_P_FIP);
- fc->fip_packet_type.dev = fc->real_dev;
- dev_add_pack(&fc->fip_packet_type);
-
return 0;
}
@@ -415,86 +514,140 @@ static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost,
return 0;
}
+/*
+ * fcoe_oem_match() - match for read types IO
+ * @fp: the fc_frame for new IO.
+ *
+ * Returns : true for read types IO, otherwise returns false.
+ */
+bool fcoe_oem_match(struct fc_frame *fp)
+{
+ return fc_fcp_is_read(fr_fsp(fp)) &&
+ (fr_fsp(fp)->data_len > fcoe_ddp_min);
+}
+
/**
* fcoe_em_config() - allocates em for this lport
- * @lp: the port that em is to allocated for
+ * @lp: the fcoe that em is to allocated for
*
* Returns : 0 on success
*/
static inline int fcoe_em_config(struct fc_lport *lp)
{
- BUG_ON(lp->emp);
+ struct fcoe_port *port = lport_priv(lp);
+ struct fcoe_interface *fcoe = port->fcoe;
+ struct fcoe_interface *oldfcoe = NULL;
+ struct net_device *old_real_dev, *cur_real_dev;
+ u16 min_xid = FCOE_MIN_XID;
+ u16 max_xid = FCOE_MAX_XID;
- lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
- FCOE_MIN_XID, FCOE_MAX_XID);
- if (!lp->emp)
+ /*
+ * Check if need to allocate an em instance for
+ * offload exchange ids to be shared across all VN_PORTs/lport.
+ */
+ if (!lp->lro_enabled || !lp->lro_xid || (lp->lro_xid >= max_xid)) {
+ lp->lro_xid = 0;
+ goto skip_oem;
+ }
+
+ /*
+ * Reuse existing offload em instance in case
+ * it is already allocated on real eth device
+ */
+ if (fcoe->netdev->priv_flags & IFF_802_1Q_VLAN)
+ cur_real_dev = vlan_dev_real_dev(fcoe->netdev);
+ else
+ cur_real_dev = fcoe->netdev;
+
+ list_for_each_entry(oldfcoe, &fcoe_hostlist, list) {
+ if (oldfcoe->netdev->priv_flags & IFF_802_1Q_VLAN)
+ old_real_dev = vlan_dev_real_dev(oldfcoe->netdev);
+ else
+ old_real_dev = oldfcoe->netdev;
+
+ if (cur_real_dev == old_real_dev) {
+ fcoe->oem = oldfcoe->oem;
+ break;
+ }
+ }
+
+ if (fcoe->oem) {
+ if (!fc_exch_mgr_add(lp, fcoe->oem, fcoe_oem_match)) {
+ printk(KERN_ERR "fcoe_em_config: failed to add "
+ "offload em:%p on interface:%s\n",
+ fcoe->oem, fcoe->netdev->name);
+ return -ENOMEM;
+ }
+ } else {
+ fcoe->oem = fc_exch_mgr_alloc(lp, FC_CLASS_3,
+ FCOE_MIN_XID, lp->lro_xid,
+ fcoe_oem_match);
+ if (!fcoe->oem) {
+ printk(KERN_ERR "fcoe_em_config: failed to allocate "
+ "em for offload exches on interface:%s\n",
+ fcoe->netdev->name);
+ return -ENOMEM;
+ }
+ }
+
+ /*
+ * Exclude offload EM xid range from next EM xid range.
+ */
+ min_xid += lp->lro_xid + 1;
+
+skip_oem:
+ if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, min_xid, max_xid, NULL)) {
+ printk(KERN_ERR "fcoe_em_config: failed to "
+ "allocate em on interface %s\n", fcoe->netdev->name);
return -ENOMEM;
+ }
return 0;
}
/**
* fcoe_if_destroy() - FCoE software HBA tear-down function
- * @netdev: ptr to the associated net_device
- *
- * Returns: 0 if link is OK for use by FCoE.
+ * @lport: fc_lport to destroy
*/
-static int fcoe_if_destroy(struct net_device *netdev)
+static void fcoe_if_destroy(struct fc_lport *lport)
{
- struct fc_lport *lp = NULL;
- struct fcoe_softc *fc;
-
- BUG_ON(!netdev);
+ struct fcoe_port *port = lport_priv(lport);
+ struct fcoe_interface *fcoe = port->fcoe;
+ struct net_device *netdev = fcoe->netdev;
FCOE_NETDEV_DBG(netdev, "Destroying interface\n");
- lp = fcoe_hostlist_lookup(netdev);
- if (!lp)
- return -ENODEV;
-
- fc = lport_priv(lp);
-
/* Logout of the fabric */
- fc_fabric_logoff(lp);
+ fc_fabric_logoff(lport);
- /* Remove the instance from fcoe's list */
- fcoe_hostlist_remove(lp);
+ /* Cleanup the fc_lport */
+ fc_lport_destroy(lport);
+ fc_fcp_destroy(lport);
- /* clean up netdev configurations */
- fcoe_netdev_cleanup(fc);
+ /* Stop the transmit retry timer */
+ del_timer_sync(&port->timer);
- /* tear-down the FCoE controller */
- fcoe_ctlr_destroy(&fc->ctlr);
+ /* Free existing transmit skbs */
+ fcoe_clean_pending_queue(lport);
- /* Cleanup the fc_lport */
- fc_lport_destroy(lp);
- fc_fcp_destroy(lp);
+ /* receives may not be stopped until after this */
+ fcoe_interface_put(fcoe);
+
+ /* Free queued packets for the per-CPU receive threads */
+ fcoe_percpu_clean(lport);
/* Detach from the scsi-ml */
- fc_remove_host(lp->host);
- scsi_remove_host(lp->host);
+ fc_remove_host(lport->host);
+ scsi_remove_host(lport->host);
/* There are no more rports or I/O, free the EM */
- if (lp->emp)
- fc_exch_mgr_free(lp->emp);
-
- /* Free the per-CPU receive threads */
- fcoe_percpu_clean(lp);
-
- /* Free existing skbs */
- fcoe_clean_pending_queue(lp);
-
- /* Stop the timer */
- del_timer_sync(&fc->timer);
+ fc_exch_mgr_free(lport);
/* Free memory used by statistical counters */
- fc_lport_free_stats(lp);
-
- /* Release the net_device and Scsi_Host */
- dev_put(fc->real_dev);
- scsi_host_put(lp->host);
+ fc_lport_free_stats(lport);
- return 0;
+ /* Release the Scsi_Host */
+ scsi_host_put(lport->host);
}
/*
@@ -540,106 +693,96 @@ static struct libfc_function_template fcoe_libfc_fcn_templ = {
};
/**
- * fcoe_if_create() - this function creates the fcoe interface
- * @netdev: pointer the associated netdevice
+ * fcoe_if_create() - this function creates the fcoe port
+ * @fcoe: fcoe_interface structure to create an fc_lport instance on
+ * @parent: device pointer to be the parent in sysfs for the SCSI host
*
- * Creates fc_lport struct and scsi_host for lport, configures lport
- * and starts fabric login.
+ * Creates fc_lport struct and scsi_host for lport, configures lport.
*
- * Returns : 0 on success
+ * Returns : The allocated fc_lport or an error pointer
*/
-static int fcoe_if_create(struct net_device *netdev)
+static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
+ struct device *parent)
{
int rc;
- struct fc_lport *lp = NULL;
- struct fcoe_softc *fc;
+ struct fc_lport *lport = NULL;
+ struct fcoe_port *port;
struct Scsi_Host *shost;
-
- BUG_ON(!netdev);
+ struct net_device *netdev = fcoe->netdev;
FCOE_NETDEV_DBG(netdev, "Create Interface\n");
- lp = fcoe_hostlist_lookup(netdev);
- if (lp)
- return -EEXIST;
-
shost = libfc_host_alloc(&fcoe_shost_template,
- sizeof(struct fcoe_softc));
+ sizeof(struct fcoe_port));
if (!shost) {
FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n");
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto out;
}
- lp = shost_priv(shost);
- fc = lport_priv(lp);
+ lport = shost_priv(shost);
+ port = lport_priv(lport);
+ port->lport = lport;
+ port->fcoe = fcoe;
+ INIT_WORK(&port->destroy_work, fcoe_destroy_work);
/* configure fc_lport, e.g., em */
- rc = fcoe_lport_config(lp);
+ rc = fcoe_lport_config(lport);
if (rc) {
FCOE_NETDEV_DBG(netdev, "Could not configure lport for the "
"interface\n");
goto out_host_put;
}
- /*
- * Initialize FIP.
- */
- fcoe_ctlr_init(&fc->ctlr);
- fc->ctlr.send = fcoe_fip_send;
- fc->ctlr.update_mac = fcoe_update_src_mac;
-
/* configure lport network properties */
- rc = fcoe_netdev_config(lp, netdev);
+ rc = fcoe_netdev_config(lport, netdev);
if (rc) {
FCOE_NETDEV_DBG(netdev, "Could not configure netdev for the "
"interface\n");
- goto out_netdev_cleanup;
+ goto out_lp_destroy;
}
/* configure lport scsi host properties */
- rc = fcoe_shost_config(lp, shost, &netdev->dev);
+ rc = fcoe_shost_config(lport, shost, parent);
if (rc) {
FCOE_NETDEV_DBG(netdev, "Could not configure shost for the "
"interface\n");
- goto out_netdev_cleanup;
- }
-
- /* lport exch manager allocation */
- rc = fcoe_em_config(lp);
- if (rc) {
- FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the "
- "interface\n");
- goto out_netdev_cleanup;
+ goto out_lp_destroy;
}
/* Initialize the library */
- rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ);
+ rc = fcoe_libfc_config(lport, &fcoe_libfc_fcn_templ);
if (rc) {
FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the "
"interface\n");
goto out_lp_destroy;
}
- /* add to lports list */
- fcoe_hostlist_add(lp);
-
- lp->boot_time = jiffies;
-
- fc_fabric_login(lp);
-
- if (!fcoe_link_ok(lp))
- fcoe_ctlr_link_up(&fc->ctlr);
+ /*
+ * fcoe_em_alloc() and fcoe_hostlist_add() both
+ * need to be atomic with respect to other changes to the hostlist
+ * since fcoe_em_alloc() looks for an existing EM
+ * instance on host list updated by fcoe_hostlist_add().
+ *
+ * This is currently handled through the fcoe_config_mutex begin held.
+ */
- dev_hold(netdev);
+ /* lport exch manager allocation */
+ rc = fcoe_em_config(lport);
+ if (rc) {
+ FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the "
+ "interface\n");
+ goto out_lp_destroy;
+ }
- return rc;
+ fcoe_interface_get(fcoe);
+ return lport;
out_lp_destroy:
- fc_exch_mgr_free(lp->emp); /* Free the EM */
-out_netdev_cleanup:
- fcoe_netdev_cleanup(fc);
+ fc_exch_mgr_free(lport);
out_host_put:
- scsi_host_put(lp->host);
- return rc;
+ scsi_host_put(lport->host);
+out:
+ return ERR_PTR(rc);
}
/**
@@ -669,6 +812,7 @@ static int __init fcoe_if_init(void)
int __exit fcoe_if_exit(void)
{
fc_release_transport(scsi_transport_fcoe_sw);
+ scsi_transport_fcoe_sw = NULL;
return 0;
}
@@ -686,7 +830,7 @@ static void fcoe_percpu_thread_create(unsigned int cpu)
thread = kthread_create(fcoe_percpu_receive_thread,
(void *)p, "fcoethread/%d", cpu);
- if (likely(!IS_ERR(p->thread))) {
+ if (likely(!IS_ERR(thread))) {
kthread_bind(thread, cpu);
wake_up_process(thread);
@@ -838,14 +982,13 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
{
struct fc_lport *lp;
struct fcoe_rcv_info *fr;
- struct fcoe_softc *fc;
+ struct fcoe_interface *fcoe;
struct fc_frame_header *fh;
struct fcoe_percpu_s *fps;
- unsigned short oxid;
- unsigned int cpu = 0;
+ unsigned int cpu;
- fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type);
- lp = fc->ctlr.lp;
+ fcoe = container_of(ptype, struct fcoe_interface, fcoe_packet_type);
+ lp = fcoe->ctlr.lp;
if (unlikely(lp == NULL)) {
FCOE_NETDEV_DBG(dev, "Cannot find hba structure");
goto err2;
@@ -876,20 +1019,20 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev,
skb_set_transport_header(skb, sizeof(struct fcoe_hdr));
fh = (struct fc_frame_header *) skb_transport_header(skb);
- oxid = ntohs(fh->fh_ox_id);
-
fr = fcoe_dev_from_skb(skb);
fr->fr_dev = lp;
fr->ptype = ptype;
-#ifdef CONFIG_SMP
/*
- * The incoming frame exchange id(oxid) is ANDed with num of online
- * cpu bits to get cpu and then this cpu is used for selecting
- * a per cpu kernel thread from fcoe_percpu.
+ * In case the incoming frame's exchange is originated from
+ * the initiator, then received frame's exchange id is ANDed
+ * with fc_cpu_mask bits to get the same cpu on which exchange
+ * was originated, otherwise just use the current cpu.
*/
- cpu = oxid & (num_online_cpus() - 1);
-#endif
+ if (ntoh24(fh->fh_f_ctl) & FC_FC_EX_CTX)
+ cpu = ntohs(fh->fh_ox_id) & fc_cpu_mask;
+ else
+ cpu = smp_processor_id();
fps = &per_cpu(fcoe_percpu, cpu);
spin_lock_bh(&fps->fcoe_rx_list.lock);
@@ -996,7 +1139,7 @@ static int fcoe_get_paged_crc_eof(struct sk_buff *skb, int tlen)
* fcoe_fc_crc() - calculates FC CRC in this fcoe skb
* @fp: the fc_frame containing data to be checksummed
*
- * This uses crc32() to calculate the crc for fc frame
+ * This uses crc32() to calculate the crc for port frame
* Return : 32 bit crc
*/
u32 fcoe_fc_crc(struct fc_frame *fp)
@@ -1029,7 +1172,7 @@ u32 fcoe_fc_crc(struct fc_frame *fp)
/**
* fcoe_xmit() - FCoE frame transmit function
- * @lp: the associated local port
+ * @lp: the associated local fcoe
* @fp: the fc_frame to be transmitted
*
* Return : 0 for success
@@ -1046,13 +1189,13 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
unsigned int hlen; /* header length implies the version */
unsigned int tlen; /* trailer length */
unsigned int elen; /* eth header, may include vlan */
- struct fcoe_softc *fc;
+ struct fcoe_port *port = lport_priv(lp);
+ struct fcoe_interface *fcoe = port->fcoe;
u8 sof, eof;
struct fcoe_hdr *hp;
WARN_ON((fr_len(fp) % sizeof(u32)) != 0);
- fc = lport_priv(lp);
fh = fc_frame_header_get(fp);
skb = fp_skb(fp);
wlen = skb->len / FCOE_WORD_TO_BYTE;
@@ -1063,7 +1206,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
}
if (unlikely(fh->fh_r_ctl == FC_RCTL_ELS_REQ) &&
- fcoe_ctlr_els_send(&fc->ctlr, skb))
+ fcoe_ctlr_els_send(&fcoe->ctlr, skb))
return 0;
sof = fr_sof(fp);
@@ -1085,7 +1228,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
crc = fcoe_fc_crc(fp);
}
- /* copy fc crc and eof to the skb buff */
+ /* copy port crc and eof to the skb buff */
if (skb_is_nonlinear(skb)) {
skb_frag_t *frag;
if (fcoe_get_paged_crc_eof(skb, tlen)) {
@@ -1108,27 +1251,27 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
cp = NULL;
}
- /* adjust skb network/transport offsets to match mac/fcoe/fc */
+ /* adjust skb network/transport offsets to match mac/fcoe/port */
skb_push(skb, elen + hlen);
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
skb->mac_len = elen;
skb->protocol = htons(ETH_P_FCOE);
- skb->dev = fc->real_dev;
+ skb->dev = fcoe->netdev;
/* fill up mac and fcoe headers */
eh = eth_hdr(skb);
eh->h_proto = htons(ETH_P_FCOE);
- if (fc->ctlr.map_dest)
+ if (fcoe->ctlr.map_dest)
fc_fcoe_set_mac(eh->h_dest, fh->fh_d_id);
else
/* insert GW address */
- memcpy(eh->h_dest, fc->ctlr.dest_addr, ETH_ALEN);
+ memcpy(eh->h_dest, fcoe->ctlr.dest_addr, ETH_ALEN);
- if (unlikely(fc->ctlr.flogi_oxid != FC_XID_UNKNOWN))
- memcpy(eh->h_source, fc->ctlr.ctl_src_addr, ETH_ALEN);
+ if (unlikely(fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN))
+ memcpy(eh->h_source, fcoe->ctlr.ctl_src_addr, ETH_ALEN);
else
- memcpy(eh->h_source, fc->ctlr.data_src_addr, ETH_ALEN);
+ memcpy(eh->h_source, fcoe->ctlr.data_src_addr, ETH_ALEN);
hp = (struct fcoe_hdr *)(eh + 1);
memset(hp, 0, sizeof(*hp));
@@ -1136,7 +1279,6 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
hp->fcoe_sof = sof;
-#ifdef NETIF_F_FSO
/* fcoe lso, mss is in max_payload which is non-zero for FCP data */
if (lp->seq_offload && fr_max_payload(fp)) {
skb_shinfo(skb)->gso_type = SKB_GSO_FCOE;
@@ -1145,7 +1287,6 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
skb_shinfo(skb)->gso_type = 0;
skb_shinfo(skb)->gso_size = 0;
}
-#endif
/* update tx stats: regardless if LLD fails */
stats = fc_lport_get_stats(lp);
stats->TxFrames++;
@@ -1153,7 +1294,7 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
/* send down to lld */
fr_dev(fp) = lp;
- if (fc->fcoe_pending_queue.qlen)
+ if (port->fcoe_pending_queue.qlen)
fcoe_check_wait_queue(lp, skb);
else if (fcoe_start_io(skb))
fcoe_check_wait_queue(lp, skb);
@@ -1162,6 +1303,15 @@ int fcoe_xmit(struct fc_lport *lp, struct fc_frame *fp)
}
/**
+ * fcoe_percpu_flush_done() - Indicate percpu queue flush completion.
+ * @skb: the skb being completed.
+ */
+static void fcoe_percpu_flush_done(struct sk_buff *skb)
+{
+ complete(&fcoe_flush_completion);
+}
+
+/**
* fcoe_percpu_receive_thread() - recv thread per cpu
* @arg: ptr to the fcoe per cpu struct
*
@@ -1179,7 +1329,7 @@ int fcoe_percpu_receive_thread(void *arg)
struct fcoe_crc_eof crc_eof;
struct fc_frame *fp;
u8 *mac = NULL;
- struct fcoe_softc *fc;
+ struct fcoe_port *port;
struct fcoe_hdr *hp;
set_user_nice(current, -20);
@@ -1200,7 +1350,8 @@ int fcoe_percpu_receive_thread(void *arg)
fr = fcoe_dev_from_skb(skb);
lp = fr->fr_dev;
if (unlikely(lp == NULL)) {
- FCOE_NETDEV_DBG(skb->dev, "Invalid HBA Structure");
+ if (skb->destructor != fcoe_percpu_flush_done)
+ FCOE_NETDEV_DBG(skb->dev, "NULL lport in skb");
kfree_skb(skb);
continue;
}
@@ -1215,7 +1366,7 @@ int fcoe_percpu_receive_thread(void *arg)
/*
* Save source MAC address before discarding header.
*/
- fc = lport_priv(lp);
+ port = lport_priv(lp);
if (skb_is_nonlinear(skb))
skb_linearize(skb); /* not ideal */
mac = eth_hdr(skb)->h_source;
@@ -1277,7 +1428,7 @@ int fcoe_percpu_receive_thread(void *arg)
fh = fc_frame_header_get(fp);
if (fh->fh_r_ctl == FC_RCTL_DD_SOL_DATA &&
fh->fh_type == FC_TYPE_FCP) {
- fc_exch_recv(lp, lp->emp, fp);
+ fc_exch_recv(lp, fp);
continue;
}
if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) {
@@ -1293,12 +1444,12 @@ int fcoe_percpu_receive_thread(void *arg)
}
fr_flags(fp) &= ~FCPHF_CRC_UNCHECKED;
}
- if (unlikely(fc->ctlr.flogi_oxid != FC_XID_UNKNOWN) &&
- fcoe_ctlr_recv_flogi(&fc->ctlr, fp, mac)) {
+ if (unlikely(port->fcoe->ctlr.flogi_oxid != FC_XID_UNKNOWN) &&
+ fcoe_ctlr_recv_flogi(&port->fcoe->ctlr, fp, mac)) {
fc_frame_free(fp);
continue;
}
- fc_exch_recv(lp, lp->emp, fp);
+ fc_exch_recv(lp, fp);
}
return 0;
}
@@ -1318,46 +1469,46 @@ int fcoe_percpu_receive_thread(void *arg)
*/
static void fcoe_check_wait_queue(struct fc_lport *lp, struct sk_buff *skb)
{
- struct fcoe_softc *fc = lport_priv(lp);
+ struct fcoe_port *port = lport_priv(lp);
int rc;
- spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ spin_lock_bh(&port->fcoe_pending_queue.lock);
if (skb)
- __skb_queue_tail(&fc->fcoe_pending_queue, skb);
+ __skb_queue_tail(&port->fcoe_pending_queue, skb);
- if (fc->fcoe_pending_queue_active)
+ if (port->fcoe_pending_queue_active)
goto out;
- fc->fcoe_pending_queue_active = 1;
+ port->fcoe_pending_queue_active = 1;
- while (fc->fcoe_pending_queue.qlen) {
+ while (port->fcoe_pending_queue.qlen) {
/* keep qlen > 0 until fcoe_start_io succeeds */
- fc->fcoe_pending_queue.qlen++;
- skb = __skb_dequeue(&fc->fcoe_pending_queue);
+ port->fcoe_pending_queue.qlen++;
+ skb = __skb_dequeue(&port->fcoe_pending_queue);
- spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+ spin_unlock_bh(&port->fcoe_pending_queue.lock);
rc = fcoe_start_io(skb);
- spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ spin_lock_bh(&port->fcoe_pending_queue.lock);
if (rc) {
- __skb_queue_head(&fc->fcoe_pending_queue, skb);
+ __skb_queue_head(&port->fcoe_pending_queue, skb);
/* undo temporary increment above */
- fc->fcoe_pending_queue.qlen--;
+ port->fcoe_pending_queue.qlen--;
break;
}
/* undo temporary increment above */
- fc->fcoe_pending_queue.qlen--;
+ port->fcoe_pending_queue.qlen--;
}
- if (fc->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
+ if (port->fcoe_pending_queue.qlen < FCOE_LOW_QUEUE_DEPTH)
lp->qfull = 0;
- if (fc->fcoe_pending_queue.qlen && !timer_pending(&fc->timer))
- mod_timer(&fc->timer, jiffies + 2);
- fc->fcoe_pending_queue_active = 0;
+ if (port->fcoe_pending_queue.qlen && !timer_pending(&port->timer))
+ mod_timer(&port->timer, jiffies + 2);
+ port->fcoe_pending_queue_active = 0;
out:
- if (fc->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
+ if (port->fcoe_pending_queue.qlen > FCOE_MAX_QUEUE_DEPTH)
lp->qfull = 1;
- spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+ spin_unlock_bh(&port->fcoe_pending_queue.lock);
return;
}
@@ -1391,21 +1542,20 @@ static int fcoe_device_notification(struct notifier_block *notifier,
ulong event, void *ptr)
{
struct fc_lport *lp = NULL;
- struct net_device *real_dev = ptr;
- struct fcoe_softc *fc;
+ struct net_device *netdev = ptr;
+ struct fcoe_interface *fcoe;
+ struct fcoe_port *port;
struct fcoe_dev_stats *stats;
u32 link_possible = 1;
u32 mfs;
int rc = NOTIFY_OK;
- read_lock(&fcoe_hostlist_lock);
- list_for_each_entry(fc, &fcoe_hostlist, list) {
- if (fc->real_dev == real_dev) {
- lp = fc->ctlr.lp;
+ list_for_each_entry(fcoe, &fcoe_hostlist, list) {
+ if (fcoe->netdev == netdev) {
+ lp = fcoe->ctlr.lp;
break;
}
}
- read_unlock(&fcoe_hostlist_lock);
if (lp == NULL) {
rc = NOTIFY_DONE;
goto out;
@@ -1420,21 +1570,27 @@ static int fcoe_device_notification(struct notifier_block *notifier,
case NETDEV_CHANGE:
break;
case NETDEV_CHANGEMTU:
- mfs = fc->real_dev->mtu -
- (sizeof(struct fcoe_hdr) +
- sizeof(struct fcoe_crc_eof));
+ mfs = netdev->mtu - (sizeof(struct fcoe_hdr) +
+ sizeof(struct fcoe_crc_eof));
if (mfs >= FC_MIN_MAX_FRAME)
fc_set_mfs(lp, mfs);
break;
case NETDEV_REGISTER:
break;
+ case NETDEV_UNREGISTER:
+ list_del(&fcoe->list);
+ port = lport_priv(fcoe->ctlr.lp);
+ fcoe_interface_cleanup(fcoe);
+ schedule_work(&port->destroy_work);
+ goto out;
+ break;
default:
- FCOE_NETDEV_DBG(real_dev, "Unknown event %ld "
+ FCOE_NETDEV_DBG(netdev, "Unknown event %ld "
"from netdev netlink\n", event);
}
if (link_possible && !fcoe_link_ok(lp))
- fcoe_ctlr_link_up(&fc->ctlr);
- else if (fcoe_ctlr_link_down(&fc->ctlr)) {
+ fcoe_ctlr_link_up(&fcoe->ctlr);
+ else if (fcoe_ctlr_link_down(&fcoe->ctlr)) {
stats = fc_lport_get_stats(lp);
stats->LinkFailureCount++;
fcoe_clean_pending_queue(lp);
@@ -1465,75 +1621,6 @@ static struct net_device *fcoe_if_to_netdev(const char *buffer)
}
/**
- * fcoe_netdev_to_module_owner() - finds out the driver module of the netdev
- * @netdev: the target netdev
- *
- * Returns: ptr to the struct module, NULL for failure
- */
-static struct module *
-fcoe_netdev_to_module_owner(const struct net_device *netdev)
-{
- struct device *dev;
-
- if (!netdev)
- return NULL;
-
- dev = netdev->dev.parent;
- if (!dev)
- return NULL;
-
- if (!dev->driver)
- return NULL;
-
- return dev->driver->owner;
-}
-
-/**
- * fcoe_ethdrv_get() - Hold the Ethernet driver
- * @netdev: the target netdev
- *
- * Holds the Ethernet driver module by try_module_get() for
- * the corresponding netdev.
- *
- * Returns: 0 for success
- */
-static int fcoe_ethdrv_get(const struct net_device *netdev)
-{
- struct module *owner;
-
- owner = fcoe_netdev_to_module_owner(netdev);
- if (owner) {
- FCOE_NETDEV_DBG(netdev, "Hold driver module %s\n",
- module_name(owner));
- return try_module_get(owner);
- }
- return -ENODEV;
-}
-
-/**
- * fcoe_ethdrv_put() - Release the Ethernet driver
- * @netdev: the target netdev
- *
- * Releases the Ethernet driver module by module_put for
- * the corresponding netdev.
- *
- * Returns: 0 for success
- */
-static int fcoe_ethdrv_put(const struct net_device *netdev)
-{
- struct module *owner;
-
- owner = fcoe_netdev_to_module_owner(netdev);
- if (owner) {
- FCOE_NETDEV_DBG(netdev, "Release driver module %s\n",
- module_name(owner));
- module_put(owner);
- return 0;
- }
- return -ENODEV;
-}
-
-/**
* fcoe_destroy() - handles the destroy from sysfs
* @buffer: expected to be an eth if name
* @kp: associated kernel param
@@ -1542,34 +1629,57 @@ static int fcoe_ethdrv_put(const struct net_device *netdev)
*/
static int fcoe_destroy(const char *buffer, struct kernel_param *kp)
{
- int rc;
+ struct fcoe_interface *fcoe;
struct net_device *netdev;
+ int rc;
+
+ mutex_lock(&fcoe_config_mutex);
+#ifdef CONFIG_FCOE_MODULE
+ /*
+ * Make sure the module has been initialized, and is not about to be
+ * removed. Module paramter sysfs files are writable before the
+ * module_init function is called and after module_exit.
+ */
+ if (THIS_MODULE->state != MODULE_STATE_LIVE) {
+ rc = -ENODEV;
+ goto out_nodev;
+ }
+#endif
netdev = fcoe_if_to_netdev(buffer);
if (!netdev) {
rc = -ENODEV;
goto out_nodev;
}
- /* look for existing lport */
- if (!fcoe_hostlist_lookup(netdev)) {
+
+ rtnl_lock();
+ fcoe = fcoe_hostlist_lookup_port(netdev);
+ if (!fcoe) {
+ rtnl_unlock();
rc = -ENODEV;
goto out_putdev;
}
- rc = fcoe_if_destroy(netdev);
- if (rc) {
- printk(KERN_ERR "fcoe: Failed to destroy interface (%s)\n",
- netdev->name);
- rc = -EIO;
- goto out_putdev;
- }
- fcoe_ethdrv_put(netdev);
- rc = 0;
+ list_del(&fcoe->list);
+ fcoe_interface_cleanup(fcoe);
+ rtnl_unlock();
+ fcoe_if_destroy(fcoe->ctlr.lp);
out_putdev:
dev_put(netdev);
out_nodev:
+ mutex_unlock(&fcoe_config_mutex);
return rc;
}
+static void fcoe_destroy_work(struct work_struct *work)
+{
+ struct fcoe_port *port;
+
+ port = container_of(work, struct fcoe_port, destroy_work);
+ mutex_lock(&fcoe_config_mutex);
+ fcoe_if_destroy(port->lport);
+ mutex_unlock(&fcoe_config_mutex);
+}
+
/**
* fcoe_create() - Handles the create call from sysfs
* @buffer: expected to be an eth if name
@@ -1580,41 +1690,84 @@ out_nodev:
static int fcoe_create(const char *buffer, struct kernel_param *kp)
{
int rc;
+ struct fcoe_interface *fcoe;
+ struct fc_lport *lport;
struct net_device *netdev;
+ mutex_lock(&fcoe_config_mutex);
+#ifdef CONFIG_FCOE_MODULE
+ /*
+ * Make sure the module has been initialized, and is not about to be
+ * removed. Module paramter sysfs files are writable before the
+ * module_init function is called and after module_exit.
+ */
+ if (THIS_MODULE->state != MODULE_STATE_LIVE) {
+ rc = -ENODEV;
+ goto out_nodev;
+ }
+#endif
+
+ rtnl_lock();
netdev = fcoe_if_to_netdev(buffer);
if (!netdev) {
rc = -ENODEV;
goto out_nodev;
}
+
/* look for existing lport */
if (fcoe_hostlist_lookup(netdev)) {
rc = -EEXIST;
goto out_putdev;
}
- fcoe_ethdrv_get(netdev);
- rc = fcoe_if_create(netdev);
- if (rc) {
+ fcoe = fcoe_interface_create(netdev);
+ if (!fcoe) {
+ rc = -ENOMEM;
+ goto out_putdev;
+ }
+
+ lport = fcoe_if_create(fcoe, &netdev->dev);
+ if (IS_ERR(lport)) {
printk(KERN_ERR "fcoe: Failed to create interface (%s)\n",
netdev->name);
- fcoe_ethdrv_put(netdev);
rc = -EIO;
- goto out_putdev;
+ fcoe_interface_cleanup(fcoe);
+ goto out_free;
}
+
+ /* Make this the "master" N_Port */
+ fcoe->ctlr.lp = lport;
+
+ /* add to lports list */
+ fcoe_hostlist_add(lport);
+
+ /* start FIP Discovery and FLOGI */
+ lport->boot_time = jiffies;
+ fc_fabric_login(lport);
+ if (!fcoe_link_ok(lport))
+ fcoe_ctlr_link_up(&fcoe->ctlr);
+
rc = 0;
+out_free:
+ /*
+ * Release from init in fcoe_interface_create(), on success lport
+ * should be holding a reference taken in fcoe_if_create().
+ */
+ fcoe_interface_put(fcoe);
out_putdev:
dev_put(netdev);
out_nodev:
+ rtnl_unlock();
+ mutex_unlock(&fcoe_config_mutex);
return rc;
}
module_param_call(create, fcoe_create, NULL, NULL, S_IWUSR);
__MODULE_PARM_TYPE(create, "string");
-MODULE_PARM_DESC(create, "Create fcoe port using net device passed in.");
+MODULE_PARM_DESC(create, "Create fcoe fcoe using net device passed in.");
module_param_call(destroy, fcoe_destroy, NULL, NULL, S_IWUSR);
__MODULE_PARM_TYPE(destroy, "string");
-MODULE_PARM_DESC(destroy, "Destroy fcoe port");
+MODULE_PARM_DESC(destroy, "Destroy fcoe fcoe");
/**
* fcoe_link_ok() - Check if link is ok for the fc_lport
@@ -1632,37 +1785,40 @@ MODULE_PARM_DESC(destroy, "Destroy fcoe port");
*/
int fcoe_link_ok(struct fc_lport *lp)
{
- struct fcoe_softc *fc = lport_priv(lp);
- struct net_device *dev = fc->real_dev;
+ struct fcoe_port *port = lport_priv(lp);
+ struct net_device *dev = port->fcoe->netdev;
struct ethtool_cmd ecmd = { ETHTOOL_GSET };
- int rc = 0;
- if ((dev->flags & IFF_UP) && netif_carrier_ok(dev)) {
- dev = fc->phys_dev;
- if (dev->ethtool_ops->get_settings) {
- dev->ethtool_ops->get_settings(dev, &ecmd);
- lp->link_supported_speeds &=
- ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
- if (ecmd.supported & (SUPPORTED_1000baseT_Half |
- SUPPORTED_1000baseT_Full))
- lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
- if (ecmd.supported & SUPPORTED_10000baseT_Full)
- lp->link_supported_speeds |=
- FC_PORTSPEED_10GBIT;
- if (ecmd.speed == SPEED_1000)
- lp->link_speed = FC_PORTSPEED_1GBIT;
- if (ecmd.speed == SPEED_10000)
- lp->link_speed = FC_PORTSPEED_10GBIT;
- }
- } else
- rc = -1;
+ if ((dev->flags & IFF_UP) && netif_carrier_ok(dev) &&
+ (!dev_ethtool_get_settings(dev, &ecmd))) {
+ lp->link_supported_speeds &=
+ ~(FC_PORTSPEED_1GBIT | FC_PORTSPEED_10GBIT);
+ if (ecmd.supported & (SUPPORTED_1000baseT_Half |
+ SUPPORTED_1000baseT_Full))
+ lp->link_supported_speeds |= FC_PORTSPEED_1GBIT;
+ if (ecmd.supported & SUPPORTED_10000baseT_Full)
+ lp->link_supported_speeds |=
+ FC_PORTSPEED_10GBIT;
+ if (ecmd.speed == SPEED_1000)
+ lp->link_speed = FC_PORTSPEED_1GBIT;
+ if (ecmd.speed == SPEED_10000)
+ lp->link_speed = FC_PORTSPEED_10GBIT;
- return rc;
+ return 0;
+ }
+ return -1;
}
/**
* fcoe_percpu_clean() - Clear the pending skbs for an lport
* @lp: the fc_lport
+ *
+ * Must be called with fcoe_create_mutex held to single-thread completion.
+ *
+ * This flushes the pending skbs by adding a new skb to each queue and
+ * waiting until they are all freed. This assures us that not only are
+ * there no packets that will be handled by the lport, but also that any
+ * threads already handling packet have returned.
*/
void fcoe_percpu_clean(struct fc_lport *lp)
{
@@ -1687,7 +1843,25 @@ void fcoe_percpu_clean(struct fc_lport *lp)
kfree_skb(skb);
}
}
+
+ if (!pp->thread || !cpu_online(cpu)) {
+ spin_unlock_bh(&pp->fcoe_rx_list.lock);
+ continue;
+ }
+
+ skb = dev_alloc_skb(0);
+ if (!skb) {
+ spin_unlock_bh(&pp->fcoe_rx_list.lock);
+ continue;
+ }
+ skb->destructor = fcoe_percpu_flush_done;
+
+ __skb_queue_tail(&pp->fcoe_rx_list, skb);
+ if (pp->fcoe_rx_list.qlen == 1)
+ wake_up_process(pp->thread);
spin_unlock_bh(&pp->fcoe_rx_list.lock);
+
+ wait_for_completion(&fcoe_flush_completion);
}
}
@@ -1699,16 +1873,16 @@ void fcoe_percpu_clean(struct fc_lport *lp)
*/
void fcoe_clean_pending_queue(struct fc_lport *lp)
{
- struct fcoe_softc *fc = lport_priv(lp);
+ struct fcoe_port *port = lport_priv(lp);
struct sk_buff *skb;
- spin_lock_bh(&fc->fcoe_pending_queue.lock);
- while ((skb = __skb_dequeue(&fc->fcoe_pending_queue)) != NULL) {
- spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+ spin_lock_bh(&port->fcoe_pending_queue.lock);
+ while ((skb = __skb_dequeue(&port->fcoe_pending_queue)) != NULL) {
+ spin_unlock_bh(&port->fcoe_pending_queue.lock);
kfree_skb(skb);
- spin_lock_bh(&fc->fcoe_pending_queue.lock);
+ spin_lock_bh(&port->fcoe_pending_queue.lock);
}
- spin_unlock_bh(&fc->fcoe_pending_queue.lock);
+ spin_unlock_bh(&port->fcoe_pending_queue.lock);
}
/**
@@ -1725,24 +1899,21 @@ int fcoe_reset(struct Scsi_Host *shost)
}
/**
- * fcoe_hostlist_lookup_softc() - find the corresponding lport by a given device
+ * fcoe_hostlist_lookup_port() - find the corresponding lport by a given device
* @dev: this is currently ptr to net_device
*
- * Returns: NULL or the located fcoe_softc
+ * Returns: NULL or the located fcoe_port
+ * Locking: must be called with the RNL mutex held
*/
-static struct fcoe_softc *
-fcoe_hostlist_lookup_softc(const struct net_device *dev)
+static struct fcoe_interface *
+fcoe_hostlist_lookup_port(const struct net_device *dev)
{
- struct fcoe_softc *fc;
+ struct fcoe_interface *fcoe;
- read_lock(&fcoe_hostlist_lock);
- list_for_each_entry(fc, &fcoe_hostlist, list) {
- if (fc->real_dev == dev) {
- read_unlock(&fcoe_hostlist_lock);
- return fc;
- }
+ list_for_each_entry(fcoe, &fcoe_hostlist, list) {
+ if (fcoe->netdev == dev)
+ return fcoe;
}
- read_unlock(&fcoe_hostlist_lock);
return NULL;
}
@@ -1751,14 +1922,14 @@ fcoe_hostlist_lookup_softc(const struct net_device *dev)
* @netdev: ptr to net_device
*
* Returns: 0 for success
+ * Locking: must be called with the RTNL mutex held
*/
-struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
+static struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
{
- struct fcoe_softc *fc;
-
- fc = fcoe_hostlist_lookup_softc(netdev);
+ struct fcoe_interface *fcoe;
- return (fc) ? fc->ctlr.lp : NULL;
+ fcoe = fcoe_hostlist_lookup_port(netdev);
+ return (fcoe) ? fcoe->ctlr.lp : NULL;
}
/**
@@ -1766,41 +1937,23 @@ struct fc_lport *fcoe_hostlist_lookup(const struct net_device *netdev)
* @lp: ptr to the fc_lport to be added
*
* Returns: 0 for success
+ * Locking: must be called with the RTNL mutex held
*/
-int fcoe_hostlist_add(const struct fc_lport *lp)
+static int fcoe_hostlist_add(const struct fc_lport *lport)
{
- struct fcoe_softc *fc;
-
- fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp));
- if (!fc) {
- fc = lport_priv(lp);
- write_lock_bh(&fcoe_hostlist_lock);
- list_add_tail(&fc->list, &fcoe_hostlist);
- write_unlock_bh(&fcoe_hostlist_lock);
+ struct fcoe_interface *fcoe;
+ struct fcoe_port *port;
+
+ fcoe = fcoe_hostlist_lookup_port(fcoe_netdev(lport));
+ if (!fcoe) {
+ port = lport_priv(lport);
+ fcoe = port->fcoe;
+ list_add_tail(&fcoe->list, &fcoe_hostlist);
}
return 0;
}
/**
- * fcoe_hostlist_remove() - remove a lport from lports list
- * @lp: ptr to the fc_lport to be removed
- *
- * Returns: 0 for success
- */
-int fcoe_hostlist_remove(const struct fc_lport *lp)
-{
- struct fcoe_softc *fc;
-
- fc = fcoe_hostlist_lookup_softc(fcoe_netdev(lp));
- BUG_ON(!fc);
- write_lock_bh(&fcoe_hostlist_lock);
- list_del(&fc->list);
- write_unlock_bh(&fcoe_hostlist_lock);
-
- return 0;
-}
-
-/**
* fcoe_init() - fcoe module loading initialization
*
* Returns 0 on success, negative on failure
@@ -1811,8 +1964,7 @@ static int __init fcoe_init(void)
int rc = 0;
struct fcoe_percpu_s *p;
- INIT_LIST_HEAD(&fcoe_hostlist);
- rwlock_init(&fcoe_hostlist_lock);
+ mutex_lock(&fcoe_config_mutex);
for_each_possible_cpu(cpu) {
p = &per_cpu(fcoe_percpu, cpu);
@@ -1830,15 +1982,18 @@ static int __init fcoe_init(void)
/* Setup link change notification */
fcoe_dev_setup();
- fcoe_if_init();
+ rc = fcoe_if_init();
+ if (rc)
+ goto out_free;
+ mutex_unlock(&fcoe_config_mutex);
return 0;
out_free:
for_each_online_cpu(cpu) {
fcoe_percpu_thread_destroy(cpu);
}
-
+ mutex_unlock(&fcoe_config_mutex);
return rc;
}
module_init(fcoe_init);
@@ -1851,21 +2006,36 @@ module_init(fcoe_init);
static void __exit fcoe_exit(void)
{
unsigned int cpu;
- struct fcoe_softc *fc, *tmp;
+ struct fcoe_interface *fcoe, *tmp;
+ struct fcoe_port *port;
+
+ mutex_lock(&fcoe_config_mutex);
fcoe_dev_cleanup();
/* releases the associated fcoe hosts */
- list_for_each_entry_safe(fc, tmp, &fcoe_hostlist, list)
- fcoe_if_destroy(fc->real_dev);
+ rtnl_lock();
+ list_for_each_entry_safe(fcoe, tmp, &fcoe_hostlist, list) {
+ list_del(&fcoe->list);
+ port = lport_priv(fcoe->ctlr.lp);
+ fcoe_interface_cleanup(fcoe);
+ schedule_work(&port->destroy_work);
+ }
+ rtnl_unlock();
unregister_hotcpu_notifier(&fcoe_cpu_notifier);
- for_each_online_cpu(cpu) {
+ for_each_online_cpu(cpu)
fcoe_percpu_thread_destroy(cpu);
- }
- /* detach from scsi transport */
+ mutex_unlock(&fcoe_config_mutex);
+
+ /* flush any asyncronous interface destroys,
+ * this should happen after the netdev notifier is unregistered */
+ flush_scheduled_work();
+
+ /* detach from scsi transport
+ * must happen after all destroys are done, therefor after the flush */
fcoe_if_exit();
}
module_exit(fcoe_exit);
diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h
index 0d724fa0898..ce7f60fb1bc 100644
--- a/drivers/scsi/fcoe/fcoe.h
+++ b/drivers/scsi/fcoe/fcoe.h
@@ -37,8 +37,8 @@
#define FCOE_MAX_OUTSTANDING_COMMANDS 1024
-#define FCOE_MIN_XID 0x0001 /* the min xid supported by fcoe_sw */
-#define FCOE_MAX_XID 0x07ef /* the max xid supported by fcoe_sw */
+#define FCOE_MIN_XID 0x0000 /* the min xid supported by fcoe_sw */
+#define FCOE_MAX_XID 0x0FFF /* the max xid supported by fcoe_sw */
unsigned int fcoe_debug_logging;
module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR);
@@ -53,7 +53,7 @@ do { \
do { \
CMD; \
} while (0); \
-} while (0);
+} while (0)
#define FCOE_DBG(fmt, args...) \
FCOE_CHECK_LOGGING(FCOE_LOGGING, \
@@ -61,7 +61,7 @@ do { \
#define FCOE_NETDEV_DBG(netdev, fmt, args...) \
FCOE_CHECK_LOGGING(FCOE_NETDEV_LOGGING, \
- printk(KERN_INFO "fcoe: %s" fmt, \
+ printk(KERN_INFO "fcoe: %s: " fmt, \
netdev->name, ##args);)
/*
@@ -75,26 +75,36 @@ struct fcoe_percpu_s {
};
/*
- * the fcoe sw transport private data
+ * an FCoE interface, 1:1 with netdev
*/
-struct fcoe_softc {
+struct fcoe_interface {
struct list_head list;
- struct net_device *real_dev;
- struct net_device *phys_dev; /* device with ethtool_ops */
+ struct net_device *netdev;
struct packet_type fcoe_packet_type;
struct packet_type fip_packet_type;
+ struct fcoe_ctlr ctlr;
+ struct fc_exch_mgr *oem; /* offload exchange manager */
+ struct kref kref;
+};
+
+/*
+ * the FCoE private structure that's allocated along with the
+ * Scsi_Host and libfc fc_lport structures
+ */
+struct fcoe_port {
+ struct fcoe_interface *fcoe;
+ struct fc_lport *lport;
struct sk_buff_head fcoe_pending_queue;
u8 fcoe_pending_queue_active;
struct timer_list timer; /* queue timer */
- struct fcoe_ctlr ctlr;
+ struct work_struct destroy_work; /* to prevent rtnl deadlocks */
};
-#define fcoe_from_ctlr(fc) container_of(fc, struct fcoe_softc, ctlr)
+#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr)
-static inline struct net_device *fcoe_netdev(
- const struct fc_lport *lp)
+static inline struct net_device *fcoe_netdev(const struct fc_lport *lp)
{
- return ((struct fcoe_softc *)lport_priv(lp))->real_dev;
+ return ((struct fcoe_port *)lport_priv(lp))->fcoe->netdev;
}
#endif /* _FCOE_H_ */
diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c
index f544340d318..62a4c202607 100644
--- a/drivers/scsi/fcoe/libfcoe.c
+++ b/drivers/scsi/fcoe/libfcoe.c
@@ -69,7 +69,7 @@ do { \
do { \
CMD; \
} while (0); \
-} while (0);
+} while (0)
#define LIBFCOE_DBG(fmt, args...) \
LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING, \
@@ -148,13 +148,17 @@ static void fcoe_ctlr_reset_fcfs(struct fcoe_ctlr *fip)
*/
void fcoe_ctlr_destroy(struct fcoe_ctlr *fip)
{
- flush_work(&fip->recv_work);
+ cancel_work_sync(&fip->recv_work);
+ spin_lock_bh(&fip->fip_recv_list.lock);
+ __skb_queue_purge(&fip->fip_recv_list);
+ spin_unlock_bh(&fip->fip_recv_list.lock);
+
spin_lock_bh(&fip->lock);
fip->state = FIP_ST_DISABLED;
fcoe_ctlr_reset_fcfs(fip);
spin_unlock_bh(&fip->lock);
del_timer_sync(&fip->timer);
- flush_work(&fip->link_work);
+ cancel_work_sync(&fip->link_work);
}
EXPORT_SYMBOL(fcoe_ctlr_destroy);
@@ -413,10 +417,18 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
struct fip_mac_desc *mac;
struct fcoe_fcf *fcf;
size_t dlen;
+ u16 fip_flags;
fcf = fip->sel_fcf;
if (!fcf)
return -ENODEV;
+
+ /* set flags according to both FCF and lport's capability on SPMA */
+ fip_flags = fcf->flags;
+ fip_flags &= fip->spma ? FIP_FL_SPMA | FIP_FL_FPMA : FIP_FL_FPMA;
+ if (!fip_flags)
+ return -ENODEV;
+
dlen = sizeof(struct fip_encaps) + skb->len; /* len before push */
cap = (struct fip_encaps_head *)skb_push(skb, sizeof(*cap));
@@ -429,9 +441,7 @@ static int fcoe_ctlr_encaps(struct fcoe_ctlr *fip,
cap->fip.fip_op = htons(FIP_OP_LS);
cap->fip.fip_subcode = FIP_SC_REQ;
cap->fip.fip_dl_len = htons((dlen + sizeof(*mac)) / FIP_BPW);
- cap->fip.fip_flags = htons(FIP_FL_FPMA);
- if (fip->spma)
- cap->fip.fip_flags |= htons(FIP_FL_SPMA);
+ cap->fip.fip_flags = htons(fip_flags);
cap->encaps.fd_desc.fip_dtype = dtype;
cap->encaps.fd_desc.fip_dlen = dlen / FIP_BPW;
@@ -879,7 +889,7 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb)
stats->RxFrames++;
stats->RxWords += skb->len / FIP_BPW;
- fc_exch_recv(lp, lp->emp, fp);
+ fc_exch_recv(lp, fp);
return;
len_err:
@@ -1104,7 +1114,6 @@ static void fcoe_ctlr_timeout(unsigned long arg)
struct fcoe_fcf *sel;
struct fcoe_fcf *fcf;
unsigned long next_timer = jiffies + msecs_to_jiffies(FIP_VN_KA_PERIOD);
- DECLARE_MAC_BUF(buf);
u8 send_ctlr_ka;
u8 send_port_ka;
@@ -1128,9 +1137,8 @@ static void fcoe_ctlr_timeout(unsigned long arg)
fcf = sel; /* the old FCF may have been freed */
if (sel) {
printk(KERN_INFO "libfcoe: host%d: FIP selected "
- "Fibre-Channel Forwarder MAC %s\n",
- fip->lp->host->host_no,
- print_mac(buf, sel->fcf_mac));
+ "Fibre-Channel Forwarder MAC %pM\n",
+ fip->lp->host->host_no, sel->fcf_mac);
memcpy(fip->dest_addr, sel->fcf_mac, ETH_ALEN);
fip->port_ka_time = jiffies +
msecs_to_jiffies(FIP_VN_KA_PERIOD);
diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c
index 07e6eedb83c..50db3e36a61 100644
--- a/drivers/scsi/fnic/fnic_fcs.c
+++ b/drivers/scsi/fnic/fnic_fcs.c
@@ -115,7 +115,7 @@ void fnic_handle_frame(struct work_struct *work)
}
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
- fc_exch_recv(lp, lp->emp, fp);
+ fc_exch_recv(lp, fp);
}
}
diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c
index 2c266c01dc5..71c7bbe26d0 100644
--- a/drivers/scsi/fnic/fnic_main.c
+++ b/drivers/scsi/fnic/fnic_main.c
@@ -671,14 +671,6 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
lp->link_up = 0;
lp->tt = fnic_transport_template;
- lp->emp = fc_exch_mgr_alloc(lp, FC_CLASS_3,
- FCPIO_HOST_EXCH_RANGE_START,
- FCPIO_HOST_EXCH_RANGE_END);
- if (!lp->emp) {
- err = -ENOMEM;
- goto err_out_remove_scsi_host;
- }
-
lp->max_retry_count = fnic->config.flogi_retries;
lp->max_rport_retry_count = fnic->config.plogi_retries;
lp->service_params = (FCP_SPPF_INIT_FCN | FCP_SPPF_RD_XRDY_DIS |
@@ -693,12 +685,18 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
fc_set_wwnn(lp, fnic->config.node_wwn);
fc_set_wwpn(lp, fnic->config.port_wwn);
- fc_exch_init(lp);
fc_lport_init(lp);
+ fc_exch_init(lp);
fc_elsct_init(lp);
fc_rport_init(lp);
fc_disc_init(lp);
+ if (!fc_exch_mgr_alloc(lp, FC_CLASS_3, FCPIO_HOST_EXCH_RANGE_START,
+ FCPIO_HOST_EXCH_RANGE_END, NULL)) {
+ err = -ENOMEM;
+ goto err_out_remove_scsi_host;
+ }
+
fc_lport_config(lp);
if (fc_set_mfs(lp, fnic->config.maxdatafieldsize +
@@ -738,7 +736,7 @@ static int __devinit fnic_probe(struct pci_dev *pdev,
return 0;
err_out_free_exch_mgr:
- fc_exch_mgr_free(lp->emp);
+ fc_exch_mgr_free(lp);
err_out_remove_scsi_host:
fc_remove_host(fnic->lport->host);
scsi_remove_host(fnic->lport->host);
@@ -827,7 +825,7 @@ static void __devexit fnic_remove(struct pci_dev *pdev)
fc_remove_host(fnic->lport->host);
scsi_remove_host(fnic->lport->host);
- fc_exch_mgr_free(fnic->lport->emp);
+ fc_exch_mgr_free(fnic->lport);
vnic_dev_notify_unset(fnic->vdev);
fnic_free_vnic_resources(fnic);
fnic_free_intr(fnic);
diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
index 166d96450a0..bb2c696c006 100644
--- a/drivers/scsi/ibmvscsi/ibmvfc.c
+++ b/drivers/scsi/ibmvscsi/ibmvfc.c
@@ -4217,7 +4217,7 @@ static int ibmvfc_alloc_mem(struct ibmvfc_host *vhost)
if (!vhost->trace)
goto free_disc_buffer;
- vhost->tgt_pool = mempool_create_kzalloc_pool(IBMVFC_TGT_MEMPOOL_SZ,
+ vhost->tgt_pool = mempool_create_kmalloc_pool(IBMVFC_TGT_MEMPOOL_SZ,
sizeof(struct ibmvfc_target));
if (!vhost->tgt_pool) {
diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h
index 4b63dd6b1c8..163245a1c3e 100644
--- a/drivers/scsi/ipr.h
+++ b/drivers/scsi/ipr.h
@@ -1199,7 +1199,7 @@ struct ipr_ioa_cfg {
struct ata_host ata_host;
char ipr_cmd_label[8];
-#define IPR_CMD_LABEL "ipr_cmnd"
+#define IPR_CMD_LABEL "ipr_cmd"
struct ipr_cmnd *ipr_cmnd_list[IPR_NUM_CMD_BLKS];
u32 ipr_cmnd_list_dma[IPR_NUM_CMD_BLKS];
};
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 518dbd91df8..2b1b834a098 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -99,6 +99,27 @@ static int iscsi_sw_tcp_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,
return total_consumed;
}
+/**
+ * iscsi_sw_sk_state_check - check socket state
+ * @sk: socket
+ *
+ * If the socket is in CLOSE or CLOSE_WAIT we should
+ * not close the connection if there is still some
+ * data pending.
+ */
+static inline int iscsi_sw_sk_state_check(struct sock *sk)
+{
+ struct iscsi_conn *conn = (struct iscsi_conn*)sk->sk_user_data;
+
+ if ((sk->sk_state == TCP_CLOSE_WAIT || sk->sk_state == TCP_CLOSE) &&
+ !atomic_read(&sk->sk_rmem_alloc)) {
+ ISCSI_SW_TCP_DBG(conn, "TCP_CLOSE|TCP_CLOSE_WAIT\n");
+ iscsi_conn_failure(conn, ISCSI_ERR_TCP_CONN_CLOSE);
+ return -ECONNRESET;
+ }
+ return 0;
+}
+
static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag)
{
struct iscsi_conn *conn = sk->sk_user_data;
@@ -117,6 +138,8 @@ static void iscsi_sw_tcp_data_ready(struct sock *sk, int flag)
rd_desc.count = 1;
tcp_read_sock(sk, &rd_desc, iscsi_sw_tcp_recv);
+ iscsi_sw_sk_state_check(sk);
+
read_unlock(&sk->sk_callback_lock);
/* If we had to (atomically) map a highmem page,
@@ -137,13 +160,7 @@ static void iscsi_sw_tcp_state_change(struct sock *sk)
conn = (struct iscsi_conn*)sk->sk_user_data;
session = conn->session;
- if ((sk->sk_state == TCP_CLOSE_WAIT ||
- sk->sk_state == TCP_CLOSE) &&
- !atomic_read(&sk->sk_rmem_alloc)) {
- ISCSI_SW_TCP_DBG(conn, "iscsi_tcp_state_change: "
- "TCP_CLOSE|TCP_CLOSE_WAIT\n");
- iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
- }
+ iscsi_sw_sk_state_check(sk);
tcp_conn = conn->dd_data;
tcp_sw_conn = tcp_conn->dd_data;
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index 6fabf66972b..c48799e9dd8 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -43,47 +43,14 @@
#define FC_DISC_RETRY_LIMIT 3 /* max retries */
#define FC_DISC_RETRY_DELAY 500UL /* (msecs) delay */
-#define FC_DISC_DELAY 3
-
static void fc_disc_gpn_ft_req(struct fc_disc *);
static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *);
-static int fc_disc_new_target(struct fc_disc *, struct fc_rport *,
- struct fc_rport_identifiers *);
-static void fc_disc_del_target(struct fc_disc *, struct fc_rport *);
-static void fc_disc_done(struct fc_disc *);
+static void fc_disc_done(struct fc_disc *, enum fc_disc_event);
static void fc_disc_timeout(struct work_struct *);
-static void fc_disc_single(struct fc_disc *, struct fc_disc_port *);
+static int fc_disc_single(struct fc_lport *, struct fc_disc_port *);
static void fc_disc_restart(struct fc_disc *);
/**
- * fc_disc_lookup_rport() - lookup a remote port by port_id
- * @lport: Fibre Channel host port instance
- * @port_id: remote port port_id to match
- */
-struct fc_rport *fc_disc_lookup_rport(const struct fc_lport *lport,
- u32 port_id)
-{
- const struct fc_disc *disc = &lport->disc;
- struct fc_rport *rport, *found = NULL;
- struct fc_rport_libfc_priv *rdata;
- int disc_found = 0;
-
- list_for_each_entry(rdata, &disc->rports, peers) {
- rport = PRIV_TO_RPORT(rdata);
- if (rport->port_id == port_id) {
- disc_found = 1;
- found = rport;
- break;
- }
- }
-
- if (!disc_found)
- found = NULL;
-
- return found;
-}
-
-/**
* fc_disc_stop_rports() - delete all the remote ports associated with the lport
* @disc: The discovery job to stop rports on
*
@@ -93,70 +60,17 @@ struct fc_rport *fc_disc_lookup_rport(const struct fc_lport *lport,
void fc_disc_stop_rports(struct fc_disc *disc)
{
struct fc_lport *lport;
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata, *next;
+ struct fc_rport_priv *rdata, *next;
lport = disc->lport;
mutex_lock(&disc->disc_mutex);
- list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
- rport = PRIV_TO_RPORT(rdata);
- list_del(&rdata->peers);
- lport->tt.rport_logoff(rport);
- }
-
- list_for_each_entry_safe(rdata, next, &disc->rogue_rports, peers) {
- rport = PRIV_TO_RPORT(rdata);
- lport->tt.rport_logoff(rport);
- }
-
+ list_for_each_entry_safe(rdata, next, &disc->rports, peers)
+ lport->tt.rport_logoff(rdata);
mutex_unlock(&disc->disc_mutex);
}
/**
- * fc_disc_rport_callback() - Event handler for rport events
- * @lport: The lport which is receiving the event
- * @rport: The rport which the event has occured on
- * @event: The event that occured
- *
- * Locking Note: The rport lock should not be held when calling
- * this function.
- */
-static void fc_disc_rport_callback(struct fc_lport *lport,
- struct fc_rport *rport,
- enum fc_rport_event event)
-{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_disc *disc = &lport->disc;
-
- FC_DISC_DBG(disc, "Received a %d event for port (%6x)\n", event,
- rport->port_id);
-
- switch (event) {
- case RPORT_EV_CREATED:
- if (disc) {
- mutex_lock(&disc->disc_mutex);
- list_add_tail(&rdata->peers, &disc->rports);
- mutex_unlock(&disc->disc_mutex);
- }
- break;
- case RPORT_EV_LOGO:
- case RPORT_EV_FAILED:
- case RPORT_EV_STOP:
- mutex_lock(&disc->disc_mutex);
- mutex_lock(&rdata->rp_mutex);
- if (rdata->trans_state == FC_PORTSTATE_ROGUE)
- list_del(&rdata->peers);
- mutex_unlock(&rdata->rp_mutex);
- mutex_unlock(&disc->disc_mutex);
- break;
- default:
- break;
- }
-
-}
-
-/**
* fc_disc_recv_rscn_req() - Handle Registered State Change Notification (RSCN)
* @sp: Current sequence of the RSCN exchange
* @fp: RSCN Frame
@@ -169,8 +83,6 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
struct fc_disc *disc)
{
struct fc_lport *lport;
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata;
struct fc_els_rscn *rp;
struct fc_els_rscn_page *pp;
struct fc_seq_els_data rjt_data;
@@ -224,10 +136,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
break;
}
dp->lp = lport;
- dp->ids.port_id = ntoh24(pp->rscn_fid);
- dp->ids.port_name = -1;
- dp->ids.node_name = -1;
- dp->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ dp->port_id = ntoh24(pp->rscn_fid);
list_add_tail(&dp->peers, &disc_ports);
break;
case ELS_ADDR_FMT_AREA:
@@ -240,6 +149,19 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
}
}
lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+
+ /*
+ * If not doing a complete rediscovery, do GPN_ID on
+ * the individual ports mentioned in the list.
+ * If any of these get an error, do a full rediscovery.
+ * In any case, go through the list and free the entries.
+ */
+ list_for_each_entry_safe(dp, next, &disc_ports, peers) {
+ list_del(&dp->peers);
+ if (!redisc)
+ redisc = fc_disc_single(lport, dp);
+ kfree(dp);
+ }
if (redisc) {
FC_DISC_DBG(disc, "RSCN received: rediscovering\n");
fc_disc_restart(disc);
@@ -247,16 +169,6 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp,
FC_DISC_DBG(disc, "RSCN received: not rediscovering. "
"redisc %d state %d in_prog %d\n",
redisc, lport->state, disc->pending);
- list_for_each_entry_safe(dp, next, &disc_ports, peers) {
- list_del(&dp->peers);
- rport = lport->tt.rport_lookup(lport, dp->ids.port_id);
- if (rport) {
- rdata = rport->dd_data;
- list_del(&rdata->peers);
- lport->tt.rport_logoff(rport);
- }
- fc_disc_single(disc, dp);
- }
}
fc_frame_free(fp);
return;
@@ -308,35 +220,34 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp,
*/
static void fc_disc_restart(struct fc_disc *disc)
{
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata, *next;
- struct fc_lport *lport = disc->lport;
+ if (!disc->disc_callback)
+ return;
FC_DISC_DBG(disc, "Restarting discovery\n");
- list_for_each_entry_safe(rdata, next, &disc->rports, peers) {
- rport = PRIV_TO_RPORT(rdata);
- list_del(&rdata->peers);
- lport->tt.rport_logoff(rport);
- }
-
disc->requested = 1;
- if (!disc->pending)
- fc_disc_gpn_ft_req(disc);
+ if (disc->pending)
+ return;
+
+ /*
+ * Advance disc_id. This is an arbitrary non-zero number that will
+ * match the value in the fc_rport_priv after discovery for all
+ * freshly-discovered remote ports. Avoid wrapping to zero.
+ */
+ disc->disc_id = (disc->disc_id + 2) | 1;
+ disc->retry_count = 0;
+ fc_disc_gpn_ft_req(disc);
}
/**
* fc_disc_start() - Fibre Channel Target discovery
* @lport: FC local port
- *
- * Returns non-zero if discovery cannot be started.
+ * @disc_callback: function to be called when discovery is complete
*/
static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
enum fc_disc_event),
struct fc_lport *lport)
{
- struct fc_rport *rport;
- struct fc_rport_identifiers ids;
struct fc_disc *disc = &lport->disc;
/*
@@ -345,145 +256,47 @@ static void fc_disc_start(void (*disc_callback)(struct fc_lport *,
* and send the GPN_FT request.
*/
mutex_lock(&disc->disc_mutex);
-
disc->disc_callback = disc_callback;
-
- /*
- * If not ready, or already running discovery, just set request flag.
- */
- disc->requested = 1;
-
- if (disc->pending) {
- mutex_unlock(&disc->disc_mutex);
- return;
- }
-
- /*
- * Handle point-to-point mode as a simple discovery
- * of the remote port. Yucky, yucky, yuck, yuck!
- */
- rport = disc->lport->ptp_rp;
- if (rport) {
- ids.port_id = rport->port_id;
- ids.port_name = rport->port_name;
- ids.node_name = rport->node_name;
- ids.roles = FC_RPORT_ROLE_UNKNOWN;
- get_device(&rport->dev);
-
- if (!fc_disc_new_target(disc, rport, &ids)) {
- disc->event = DISC_EV_SUCCESS;
- fc_disc_done(disc);
- }
- put_device(&rport->dev);
- } else {
- fc_disc_gpn_ft_req(disc); /* get ports by FC-4 type */
- }
-
+ fc_disc_restart(disc);
mutex_unlock(&disc->disc_mutex);
}
-static struct fc_rport_operations fc_disc_rport_ops = {
- .event_callback = fc_disc_rport_callback,
-};
-
-/**
- * fc_disc_new_target() - Handle new target found by discovery
- * @lport: FC local port
- * @rport: The previous FC remote port (NULL if new remote port)
- * @ids: Identifiers for the new FC remote port
- *
- * Locking Note: This function expects that the disc_mutex is locked
- * before it is called.
- */
-static int fc_disc_new_target(struct fc_disc *disc,
- struct fc_rport *rport,
- struct fc_rport_identifiers *ids)
-{
- struct fc_lport *lport = disc->lport;
- struct fc_rport_libfc_priv *rdata;
- int error = 0;
-
- if (rport && ids->port_name) {
- if (rport->port_name == -1) {
- /*
- * Set WWN and fall through to notify of create.
- */
- fc_rport_set_name(rport, ids->port_name,
- rport->node_name);
- } else if (rport->port_name != ids->port_name) {
- /*
- * This is a new port with the same FCID as
- * a previously-discovered port. Presumably the old
- * port logged out and a new port logged in and was
- * assigned the same FCID. This should be rare.
- * Delete the old one and fall thru to re-create.
- */
- fc_disc_del_target(disc, rport);
- rport = NULL;
- }
- }
- if (((ids->port_name != -1) || (ids->port_id != -1)) &&
- ids->port_id != fc_host_port_id(lport->host) &&
- ids->port_name != lport->wwpn) {
- if (!rport) {
- rport = lport->tt.rport_lookup(lport, ids->port_id);
- if (!rport) {
- struct fc_disc_port dp;
- dp.lp = lport;
- dp.ids.port_id = ids->port_id;
- dp.ids.port_name = ids->port_name;
- dp.ids.node_name = ids->node_name;
- dp.ids.roles = ids->roles;
- rport = lport->tt.rport_create(&dp);
- }
- if (!rport)
- error = -ENOMEM;
- }
- if (rport) {
- rdata = rport->dd_data;
- rdata->ops = &fc_disc_rport_ops;
- rdata->rp_state = RPORT_ST_INIT;
- list_add_tail(&rdata->peers, &disc->rogue_rports);
- lport->tt.rport_login(rport);
- }
- }
- return error;
-}
-
-/**
- * fc_disc_del_target() - Delete a target
- * @disc: FC discovery context
- * @rport: The remote port to be removed
- */
-static void fc_disc_del_target(struct fc_disc *disc, struct fc_rport *rport)
-{
- struct fc_lport *lport = disc->lport;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- list_del(&rdata->peers);
- lport->tt.rport_logoff(rport);
-}
-
/**
* fc_disc_done() - Discovery has been completed
* @disc: FC discovery context
+ * @event: discovery completion status
+ *
* Locking Note: This function expects that the disc mutex is locked before
* it is called. The discovery callback is then made with the lock released,
* and the lock is re-taken before returning from this function
*/
-static void fc_disc_done(struct fc_disc *disc)
+static void fc_disc_done(struct fc_disc *disc, enum fc_disc_event event)
{
struct fc_lport *lport = disc->lport;
- enum fc_disc_event event;
+ struct fc_rport_priv *rdata;
FC_DISC_DBG(disc, "Discovery complete\n");
- event = disc->event;
- disc->event = DISC_EV_NONE;
+ disc->pending = 0;
+ if (disc->requested) {
+ fc_disc_restart(disc);
+ return;
+ }
- if (disc->requested)
- fc_disc_gpn_ft_req(disc);
- else
- disc->pending = 0;
+ /*
+ * Go through all remote ports. If they were found in the latest
+ * discovery, reverify or log them in. Otherwise, log them out.
+ * Skip ports which were never discovered. These are the dNS port
+ * and ports which were created by PLOGI.
+ */
+ list_for_each_entry(rdata, &disc->rports, peers) {
+ if (!rdata->disc_id)
+ continue;
+ if (rdata->disc_id == disc->disc_id)
+ lport->tt.rport_login(rdata);
+ else
+ lport->tt.rport_logoff(rdata);
+ }
mutex_unlock(&disc->disc_mutex);
disc->disc_callback(lport, event);
@@ -522,11 +335,8 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
}
disc->retry_count++;
schedule_delayed_work(&disc->disc_work, delay);
- } else {
- /* exceeded retries */
- disc->event = DISC_EV_FAILED;
- fc_disc_done(disc);
- }
+ } else
+ fc_disc_done(disc, DISC_EV_FAILED);
}
}
@@ -555,7 +365,7 @@ static void fc_disc_gpn_ft_req(struct fc_disc *disc)
if (!fp)
goto err;
- if (lport->tt.elsct_send(lport, NULL, fp,
+ if (lport->tt.elsct_send(lport, 0, fp,
FC_NS_GPN_FT,
fc_disc_gpn_ft_resp,
disc, lport->e_d_tov))
@@ -565,10 +375,12 @@ err:
}
/**
- * fc_disc_gpn_ft_parse() - Parse the list of IDs and names resulting from a request
+ * fc_disc_gpn_ft_parse() - Parse the body of the dNS GPN_FT response.
* @lport: Fibre Channel host port instance
* @buf: GPN_FT response buffer
* @len: size of response buffer
+ *
+ * Goes through the list of IDs and names resulting from a request.
*/
static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
{
@@ -578,11 +390,11 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
size_t plen;
size_t tlen;
int error = 0;
- struct fc_disc_port dp;
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata;
+ struct fc_rport_identifiers ids;
+ struct fc_rport_priv *rdata;
lport = disc->lport;
+ disc->seq_count++;
/*
* Handle partial name record left over from previous call.
@@ -591,6 +403,7 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
plen = len;
np = (struct fc_gpn_ft_resp *)bp;
tlen = disc->buf_len;
+ disc->buf_len = 0;
if (tlen) {
WARN_ON(tlen >= sizeof(*np));
plen = sizeof(*np) - tlen;
@@ -621,31 +434,25 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
* After the first time through the loop, things return to "normal".
*/
while (plen >= sizeof(*np)) {
- dp.lp = lport;
- dp.ids.port_id = ntoh24(np->fp_fid);
- dp.ids.port_name = ntohll(np->fp_wwpn);
- dp.ids.node_name = -1;
- dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
-
- if ((dp.ids.port_id != fc_host_port_id(lport->host)) &&
- (dp.ids.port_name != lport->wwpn)) {
- rport = lport->tt.rport_create(&dp);
- if (rport) {
- rdata = rport->dd_data;
- rdata->ops = &fc_disc_rport_ops;
- rdata->local_port = lport;
- list_add_tail(&rdata->peers,
- &disc->rogue_rports);
- lport->tt.rport_login(rport);
- } else
+ ids.port_id = ntoh24(np->fp_fid);
+ ids.port_name = ntohll(np->fp_wwpn);
+
+ if (ids.port_id != fc_host_port_id(lport->host) &&
+ ids.port_name != lport->wwpn) {
+ rdata = lport->tt.rport_create(lport, ids.port_id);
+ if (rdata) {
+ rdata->ids.port_name = ids.port_name;
+ rdata->disc_id = disc->disc_id;
+ } else {
printk(KERN_WARNING "libfc: Failed to allocate "
"memory for the newly discovered port "
- "(%6x)\n", dp.ids.port_id);
+ "(%6x)\n", ids.port_id);
+ error = -ENOMEM;
+ }
}
if (np->fp_flags & FC_NS_FID_LAST) {
- disc->event = DISC_EV_SUCCESS;
- fc_disc_done(disc);
+ fc_disc_done(disc, DISC_EV_SUCCESS);
len = 0;
break;
}
@@ -665,8 +472,6 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len)
memcpy(&disc->partial_buf, np, len);
}
disc->buf_len = (unsigned char) len;
- } else {
- disc->buf_len = 0;
}
return error;
}
@@ -683,8 +488,7 @@ static void fc_disc_timeout(struct work_struct *work)
struct fc_disc,
disc_work.work);
mutex_lock(&disc->disc_mutex);
- if (disc->requested && !disc->pending)
- fc_disc_gpn_ft_req(disc);
+ fc_disc_gpn_ft_req(disc);
mutex_unlock(&disc->disc_mutex);
}
@@ -703,10 +507,10 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_disc *disc = disc_arg;
struct fc_ct_hdr *cp;
struct fc_frame_header *fh;
+ enum fc_disc_event event = DISC_EV_NONE;
unsigned int seq_cnt;
- void *buf = NULL;
unsigned int len;
- int error;
+ int error = 0;
mutex_lock(&disc->disc_mutex);
FC_DISC_DBG(disc, "Received a GPN_FT response\n");
@@ -721,77 +525,158 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp,
fh = fc_frame_header_get(fp);
len = fr_len(fp) - sizeof(*fh);
seq_cnt = ntohs(fh->fh_seq_cnt);
- if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 &&
- disc->seq_count == 0) {
+ if (fr_sof(fp) == FC_SOF_I3 && seq_cnt == 0 && disc->seq_count == 0) {
cp = fc_frame_payload_get(fp, sizeof(*cp));
if (!cp) {
FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n",
fr_len(fp));
+ event = DISC_EV_FAILED;
} else if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
/* Accepted, parse the response. */
- buf = cp + 1;
len -= sizeof(*cp);
+ error = fc_disc_gpn_ft_parse(disc, cp + 1, len);
} else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x "
"(check zoning)\n", cp->ct_reason,
cp->ct_explan);
- disc->event = DISC_EV_FAILED;
- fc_disc_done(disc);
+ event = DISC_EV_FAILED;
+ if (cp->ct_reason == FC_FS_RJT_UNABL &&
+ cp->ct_explan == FC_FS_EXP_FTNR)
+ event = DISC_EV_SUCCESS;
} else {
FC_DISC_DBG(disc, "GPN_FT unexpected response code "
"%x\n", ntohs(cp->ct_cmd));
+ event = DISC_EV_FAILED;
}
- } else if (fr_sof(fp) == FC_SOF_N3 &&
- seq_cnt == disc->seq_count) {
- buf = fh + 1;
+ } else if (fr_sof(fp) == FC_SOF_N3 && seq_cnt == disc->seq_count) {
+ error = fc_disc_gpn_ft_parse(disc, fh + 1, len);
} else {
FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? "
"seq_cnt %x expected %x sof %x eof %x\n",
seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp));
+ event = DISC_EV_FAILED;
}
- if (buf) {
- error = fc_disc_gpn_ft_parse(disc, buf, len);
- if (error)
- fc_disc_error(disc, fp);
- else
- disc->seq_count++;
- }
+ if (error)
+ fc_disc_error(disc, fp);
+ else if (event != DISC_EV_NONE)
+ fc_disc_done(disc, event);
fc_frame_free(fp);
-
mutex_unlock(&disc->disc_mutex);
}
/**
- * fc_disc_single() - Discover the directory information for a single target
- * @lport: FC local port
- * @dp: The port to rediscover
+ * fc_disc_gpn_id_resp() - Handle a response frame from Get Port Names (GPN_ID)
+ * @sp: exchange sequence
+ * @fp: response frame
+ * @rdata_arg: remote port private data
*
- * Locking Note: This function expects that the disc_mutex is locked
- * before it is called.
+ * Locking Note: This function is called without disc mutex held.
*/
-static void fc_disc_single(struct fc_disc *disc, struct fc_disc_port *dp)
+static void fc_disc_gpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rdata_arg)
{
+ struct fc_rport_priv *rdata = rdata_arg;
+ struct fc_rport_priv *new_rdata;
struct fc_lport *lport;
- struct fc_rport *new_rport;
- struct fc_rport_libfc_priv *rdata;
+ struct fc_disc *disc;
+ struct fc_ct_hdr *cp;
+ struct fc_ns_gid_pn *pn;
+ u64 port_name;
- lport = disc->lport;
+ lport = rdata->local_port;
+ disc = &lport->disc;
- if (dp->ids.port_id == fc_host_port_id(lport->host))
+ mutex_lock(&disc->disc_mutex);
+ if (PTR_ERR(fp) == -FC_EX_CLOSED)
goto out;
-
- new_rport = lport->tt.rport_create(dp);
- if (new_rport) {
- rdata = new_rport->dd_data;
- rdata->ops = &fc_disc_rport_ops;
- kfree(dp);
- list_add_tail(&rdata->peers, &disc->rogue_rports);
- lport->tt.rport_login(new_rport);
+ if (IS_ERR(fp))
+ goto redisc;
+
+ cp = fc_frame_payload_get(fp, sizeof(*cp));
+ if (!cp)
+ goto redisc;
+ if (ntohs(cp->ct_cmd) == FC_FS_ACC) {
+ if (fr_len(fp) < sizeof(struct fc_frame_header) +
+ sizeof(*cp) + sizeof(*pn))
+ goto redisc;
+ pn = (struct fc_ns_gid_pn *)(cp + 1);
+ port_name = get_unaligned_be64(&pn->fn_wwpn);
+ if (rdata->ids.port_name == -1)
+ rdata->ids.port_name = port_name;
+ else if (rdata->ids.port_name != port_name) {
+ FC_DISC_DBG(disc, "GPN_ID accepted. WWPN changed. "
+ "Port-id %x wwpn %llx\n",
+ rdata->ids.port_id, port_name);
+ lport->tt.rport_logoff(rdata);
+
+ new_rdata = lport->tt.rport_create(lport,
+ rdata->ids.port_id);
+ if (new_rdata) {
+ new_rdata->disc_id = disc->disc_id;
+ lport->tt.rport_login(new_rdata);
+ }
+ goto out;
+ }
+ rdata->disc_id = disc->disc_id;
+ lport->tt.rport_login(rdata);
+ } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) {
+ FC_DISC_DBG(disc, "GPN_ID rejected reason %x exp %x\n",
+ cp->ct_reason, cp->ct_explan);
+ lport->tt.rport_logoff(rdata);
+ } else {
+ FC_DISC_DBG(disc, "GPN_ID unexpected response code %x\n",
+ ntohs(cp->ct_cmd));
+redisc:
+ fc_disc_restart(disc);
}
- return;
out:
- kfree(dp);
+ mutex_unlock(&disc->disc_mutex);
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
+}
+
+/**
+ * fc_disc_gpn_id_req() - Send Get Port Names by ID (GPN_ID) request
+ * @lport: local port
+ * @rdata: remote port private data
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ * On failure, an error code is returned.
+ */
+static int fc_disc_gpn_id_req(struct fc_lport *lport,
+ struct fc_rport_priv *rdata)
+{
+ struct fc_frame *fp;
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_ct_hdr) +
+ sizeof(struct fc_ns_fid));
+ if (!fp)
+ return -ENOMEM;
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, FC_NS_GPN_ID,
+ fc_disc_gpn_id_resp, rdata, lport->e_d_tov))
+ return -ENOMEM;
+ kref_get(&rdata->kref);
+ return 0;
+}
+
+/**
+ * fc_disc_single() - Discover the directory information for a single target
+ * @lport: local port
+ * @dp: The port to rediscover
+ *
+ * Locking Note: This function expects that the disc_mutex is locked
+ * before it is called.
+ */
+static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)
+{
+ struct fc_rport_priv *rdata;
+
+ rdata = lport->tt.rport_create(lport, dp->port_id);
+ if (!rdata)
+ return -ENOMEM;
+ rdata->disc_id = 0;
+ return fc_disc_gpn_id_req(lport, rdata);
}
/**
@@ -841,18 +726,12 @@ int fc_disc_init(struct fc_lport *lport)
if (!lport->tt.disc_recv_req)
lport->tt.disc_recv_req = fc_disc_recv_req;
- if (!lport->tt.rport_lookup)
- lport->tt.rport_lookup = fc_disc_lookup_rport;
-
disc = &lport->disc;
INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);
mutex_init(&disc->disc_mutex);
INIT_LIST_HEAD(&disc->rports);
- INIT_LIST_HEAD(&disc->rogue_rports);
disc->lport = lport;
- disc->delay = FC_DISC_DELAY;
- disc->event = DISC_EV_NONE;
return 0;
}
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
index 5878b34bff1..5cfa68732e9 100644
--- a/drivers/scsi/libfc/fc_elsct.c
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -32,7 +32,7 @@
* fc_elsct_send - sends ELS/CT frame
*/
static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
- struct fc_rport *rport,
+ u32 did,
struct fc_frame *fp,
unsigned int op,
void (*resp)(struct fc_seq *,
@@ -41,16 +41,17 @@ static struct fc_seq *fc_elsct_send(struct fc_lport *lport,
void *arg, u32 timer_msec)
{
enum fc_rctl r_ctl;
- u32 did = FC_FID_NONE;
enum fc_fh_type fh_type;
int rc;
/* ELS requests */
if ((op >= ELS_LS_RJT) && (op <= ELS_AUTH_ELS))
- rc = fc_els_fill(lport, rport, fp, op, &r_ctl, &did, &fh_type);
- else
+ rc = fc_els_fill(lport, did, fp, op, &r_ctl, &fh_type);
+ else {
/* CT requests */
- rc = fc_ct_fill(lport, fp, op, &r_ctl, &did, &fh_type);
+ rc = fc_ct_fill(lport, did, fp, op, &r_ctl, &fh_type);
+ did = FC_FID_DIR_SERV;
+ }
if (rc)
return NULL;
@@ -69,3 +70,41 @@ int fc_elsct_init(struct fc_lport *lport)
return 0;
}
EXPORT_SYMBOL(fc_elsct_init);
+
+/**
+ * fc_els_resp_type() - return string describing ELS response for debug.
+ * @fp: frame pointer with possible error code.
+ */
+const char *fc_els_resp_type(struct fc_frame *fp)
+{
+ const char *msg;
+ if (IS_ERR(fp)) {
+ switch (-PTR_ERR(fp)) {
+ case FC_NO_ERR:
+ msg = "response no error";
+ break;
+ case FC_EX_TIMEOUT:
+ msg = "response timeout";
+ break;
+ case FC_EX_CLOSED:
+ msg = "response closed";
+ break;
+ default:
+ msg = "response unknown error";
+ break;
+ }
+ } else {
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_LS_ACC:
+ msg = "accept";
+ break;
+ case ELS_LS_RJT:
+ msg = "reject";
+ break;
+ default:
+ msg = "response unknown ELS";
+ break;
+ }
+ }
+ return msg;
+}
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 145ab9ba55e..c1c15748220 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -32,6 +32,9 @@
#include <scsi/libfc.h>
#include <scsi/fc_encode.h>
+u16 fc_cpu_mask; /* cpu mask for possible cpus */
+EXPORT_SYMBOL(fc_cpu_mask);
+static u16 fc_cpu_order; /* 2's power to represent total possible cpus */
static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
/*
@@ -48,6 +51,20 @@ static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
*/
/*
+ * Per cpu exchange pool
+ *
+ * This structure manages per cpu exchanges in array of exchange pointers.
+ * This array is allocated followed by struct fc_exch_pool memory for
+ * assigned range of exchanges to per cpu pool.
+ */
+struct fc_exch_pool {
+ u16 next_index; /* next possible free exchange index */
+ u16 total_exches; /* total allocated exchanges */
+ spinlock_t lock; /* exch pool lock */
+ struct list_head ex_list; /* allocated exchanges list */
+};
+
+/*
* Exchange manager.
*
* This structure is the center for creating exchanges and sequences.
@@ -55,17 +72,13 @@ static struct kmem_cache *fc_em_cachep; /* cache for exchanges */
*/
struct fc_exch_mgr {
enum fc_class class; /* default class for sequences */
- spinlock_t em_lock; /* exchange manager lock,
- must be taken before ex_lock */
- u16 last_xid; /* last allocated exchange ID */
+ struct kref kref; /* exchange mgr reference count */
u16 min_xid; /* min exchange ID */
u16 max_xid; /* max exchange ID */
- u16 max_read; /* max exchange ID for read */
- u16 last_read; /* last xid allocated for read */
- u32 total_exches; /* total allocated exchanges */
struct list_head ex_list; /* allocated exchanges list */
- struct fc_lport *lp; /* fc device instance */
mempool_t *ep_pool; /* reserve ep's */
+ u16 pool_max_index; /* max exch array index in exch pool */
+ struct fc_exch_pool *pool; /* per cpu exch pool */
/*
* currently exchange mgr stats are updated but not used.
@@ -80,10 +93,15 @@ struct fc_exch_mgr {
atomic_t seq_not_found;
atomic_t non_bls_resp;
} stats;
- struct fc_exch **exches; /* for exch pointers indexed by xid */
};
#define fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)
+struct fc_exch_mgr_anchor {
+ struct list_head ema_list;
+ struct fc_exch_mgr *mp;
+ bool (*match)(struct fc_frame *);
+};
+
static void fc_exch_rrq(struct fc_exch *);
static void fc_seq_ls_acc(struct fc_seq *);
static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason,
@@ -167,8 +185,8 @@ static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp);
* sequence allocation and deallocation must be locked.
* - exchange refcnt can be done atomicly without locks.
* - sequence allocation must be locked by exch lock.
- * - If the em_lock and ex_lock must be taken at the same time, then the
- * em_lock must be taken before the ex_lock.
+ * - If the EM pool lock and ex_lock must be taken at the same time, then the
+ * EM pool lock must be taken before the ex_lock.
*/
/*
@@ -268,8 +286,6 @@ static void fc_exch_release(struct fc_exch *ep)
mp = ep->em;
if (ep->destructor)
ep->destructor(&ep->seq, ep->arg);
- if (ep->lp->tt.exch_put)
- ep->lp->tt.exch_put(ep->lp, mp, ep->xid);
WARN_ON(!(ep->esb_stat & ESB_ST_COMPLETE));
mempool_free(ep, mp->ep_pool);
}
@@ -299,17 +315,31 @@ static int fc_exch_done_locked(struct fc_exch *ep)
return rc;
}
-static void fc_exch_mgr_delete_ep(struct fc_exch *ep)
+static inline struct fc_exch *fc_exch_ptr_get(struct fc_exch_pool *pool,
+ u16 index)
{
- struct fc_exch_mgr *mp;
+ struct fc_exch **exches = (struct fc_exch **)(pool + 1);
+ return exches[index];
+}
- mp = ep->em;
- spin_lock_bh(&mp->em_lock);
- WARN_ON(mp->total_exches <= 0);
- mp->total_exches--;
- mp->exches[ep->xid - mp->min_xid] = NULL;
+static inline void fc_exch_ptr_set(struct fc_exch_pool *pool, u16 index,
+ struct fc_exch *ep)
+{
+ ((struct fc_exch **)(pool + 1))[index] = ep;
+}
+
+static void fc_exch_delete(struct fc_exch *ep)
+{
+ struct fc_exch_pool *pool;
+
+ pool = ep->pool;
+ spin_lock_bh(&pool->lock);
+ WARN_ON(pool->total_exches <= 0);
+ pool->total_exches--;
+ fc_exch_ptr_set(pool, (ep->xid - ep->em->min_xid) >> fc_cpu_order,
+ NULL);
list_del(&ep->ex_list);
- spin_unlock_bh(&mp->em_lock);
+ spin_unlock_bh(&pool->lock);
fc_exch_release(ep); /* drop hold for exch in mp */
}
@@ -322,7 +352,7 @@ static inline void fc_exch_timer_set_locked(struct fc_exch *ep,
if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
return;
- FC_EXCH_DBG(ep, "Exchange timed out, notifying the upper layer\n");
+ FC_EXCH_DBG(ep, "Exchange timer armed\n");
if (schedule_delayed_work(&ep->timeout_work,
msecs_to_jiffies(timer_msec)))
@@ -408,6 +438,8 @@ static void fc_exch_timeout(struct work_struct *work)
u32 e_stat;
int rc = 1;
+ FC_EXCH_DBG(ep, "Exchange timed out\n");
+
spin_lock_bh(&ep->ex_lock);
if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE))
goto unlock;
@@ -427,7 +459,7 @@ static void fc_exch_timeout(struct work_struct *work)
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
- fc_exch_mgr_delete_ep(ep);
+ fc_exch_delete(ep);
if (resp)
resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg);
fc_seq_exch_abort(sp, 2 * ep->r_a_tov);
@@ -460,65 +492,20 @@ static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id)
return sp;
}
-/*
- * fc_em_alloc_xid - returns an xid based on request type
- * @lp : ptr to associated lport
- * @fp : ptr to the assocated frame
+/**
+ * fc_exch_em_alloc() - allocate an exchange from a specified EM.
+ * @lport: ptr to the local port
+ * @mp: ptr to the exchange manager
*
- * check the associated fc_fsp_pkt to get scsi command type and
- * command direction to decide from which range this exch id
- * will be allocated from.
- *
- * Returns : 0 or an valid xid
+ * Returns pointer to allocated fc_exch with exch lock held.
*/
-static u16 fc_em_alloc_xid(struct fc_exch_mgr *mp, const struct fc_frame *fp)
-{
- u16 xid, min, max;
- u16 *plast;
- struct fc_exch *ep = NULL;
-
- if (mp->max_read) {
- if (fc_fcp_is_read(fr_fsp(fp))) {
- min = mp->min_xid;
- max = mp->max_read;
- plast = &mp->last_read;
- } else {
- min = mp->max_read + 1;
- max = mp->max_xid;
- plast = &mp->last_xid;
- }
- } else {
- min = mp->min_xid;
- max = mp->max_xid;
- plast = &mp->last_xid;
- }
- xid = *plast;
- do {
- xid = (xid == max) ? min : xid + 1;
- ep = mp->exches[xid - mp->min_xid];
- } while ((ep != NULL) && (xid != *plast));
-
- if (unlikely(ep))
- xid = 0;
- else
- *plast = xid;
-
- return xid;
-}
-
-/*
- * fc_exch_alloc - allocate an exchange.
- * @mp : ptr to the exchange manager
- * @xid: input xid
- *
- * if xid is supplied zero then assign next free exchange ID
- * from exchange manager, otherwise use supplied xid.
- * Returns with exch lock held.
- */
-struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
- struct fc_frame *fp, u16 xid)
+static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,
+ struct fc_exch_mgr *mp)
{
struct fc_exch *ep;
+ unsigned int cpu;
+ u16 index;
+ struct fc_exch_pool *pool;
/* allocate memory for exchange */
ep = mempool_alloc(mp->ep_pool, GFP_ATOMIC);
@@ -528,16 +515,17 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
}
memset(ep, 0, sizeof(*ep));
- spin_lock_bh(&mp->em_lock);
- /* alloc xid if input xid 0 */
- if (!xid) {
- /* alloc a new xid */
- xid = fc_em_alloc_xid(mp, fp);
- if (!xid) {
- printk(KERN_WARNING "libfc: Failed to allocate an exhange\n");
+ cpu = smp_processor_id();
+ pool = per_cpu_ptr(mp->pool, cpu);
+ spin_lock_bh(&pool->lock);
+ index = pool->next_index;
+ /* allocate new exch from pool */
+ while (fc_exch_ptr_get(pool, index)) {
+ index = index == mp->pool_max_index ? 0 : index + 1;
+ if (index == pool->next_index)
goto err;
- }
}
+ pool->next_index = index == mp->pool_max_index ? 0 : index + 1;
fc_exch_hold(ep); /* hold for exch in mp */
spin_lock_init(&ep->ex_lock);
@@ -548,18 +536,19 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
*/
spin_lock_bh(&ep->ex_lock);
- mp->exches[xid - mp->min_xid] = ep;
- list_add_tail(&ep->ex_list, &mp->ex_list);
+ fc_exch_ptr_set(pool, index, ep);
+ list_add_tail(&ep->ex_list, &pool->ex_list);
fc_seq_alloc(ep, ep->seq_id++);
- mp->total_exches++;
- spin_unlock_bh(&mp->em_lock);
+ pool->total_exches++;
+ spin_unlock_bh(&pool->lock);
/*
* update exchange
*/
- ep->oxid = ep->xid = xid;
+ ep->oxid = ep->xid = (index << fc_cpu_order | cpu) + mp->min_xid;
ep->em = mp;
- ep->lp = mp->lp;
+ ep->pool = pool;
+ ep->lp = lport;
ep->f_ctl = FC_FC_FIRST_SEQ; /* next seq is first seq */
ep->rxid = FC_XID_UNKNOWN;
ep->class = mp->class;
@@ -567,11 +556,36 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
out:
return ep;
err:
- spin_unlock_bh(&mp->em_lock);
+ spin_unlock_bh(&pool->lock);
atomic_inc(&mp->stats.no_free_exch_xid);
mempool_free(ep, mp->ep_pool);
return NULL;
}
+
+/**
+ * fc_exch_alloc() - allocate an exchange.
+ * @lport: ptr to the local port
+ * @fp: ptr to the FC frame
+ *
+ * This function walks the list of the exchange manager(EM)
+ * anchors to select a EM for new exchange allocation. The
+ * EM is selected having either a NULL match function pointer
+ * or call to match function returning true.
+ */
+struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_exch_mgr_anchor *ema;
+ struct fc_exch *ep;
+
+ list_for_each_entry(ema, &lport->ema_list, ema_list) {
+ if (!ema->match || ema->match(fp)) {
+ ep = fc_exch_em_alloc(lport, ema->mp);
+ if (ep)
+ return ep;
+ }
+ }
+ return NULL;
+}
EXPORT_SYMBOL(fc_exch_alloc);
/*
@@ -579,16 +593,18 @@ EXPORT_SYMBOL(fc_exch_alloc);
*/
static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)
{
+ struct fc_exch_pool *pool;
struct fc_exch *ep = NULL;
if ((xid >= mp->min_xid) && (xid <= mp->max_xid)) {
- spin_lock_bh(&mp->em_lock);
- ep = mp->exches[xid - mp->min_xid];
+ pool = per_cpu_ptr(mp->pool, xid & fc_cpu_mask);
+ spin_lock_bh(&pool->lock);
+ ep = fc_exch_ptr_get(pool, (xid - mp->min_xid) >> fc_cpu_order);
if (ep) {
fc_exch_hold(ep);
WARN_ON(ep->xid != xid);
}
- spin_unlock_bh(&mp->em_lock);
+ spin_unlock_bh(&pool->lock);
}
return ep;
}
@@ -602,7 +618,7 @@ void fc_exch_done(struct fc_seq *sp)
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
- fc_exch_mgr_delete_ep(ep);
+ fc_exch_delete(ep);
}
EXPORT_SYMBOL(fc_exch_done);
@@ -610,12 +626,14 @@ EXPORT_SYMBOL(fc_exch_done);
* Allocate a new exchange as responder.
* Sets the responder ID in the frame header.
*/
-static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
+static struct fc_exch *fc_exch_resp(struct fc_lport *lport,
+ struct fc_exch_mgr *mp,
+ struct fc_frame *fp)
{
struct fc_exch *ep;
struct fc_frame_header *fh;
- ep = mp->lp->tt.exch_get(mp->lp, fp);
+ ep = fc_exch_alloc(lport, fp);
if (ep) {
ep->class = fc_frame_class(fp);
@@ -641,7 +659,7 @@ static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
ep->esb_stat &= ~ESB_ST_SEQ_INIT;
fc_exch_hold(ep); /* hold for caller */
- spin_unlock_bh(&ep->ex_lock); /* lock from exch_get */
+ spin_unlock_bh(&ep->ex_lock); /* lock from fc_exch_alloc */
}
return ep;
}
@@ -651,7 +669,8 @@ static struct fc_exch *fc_exch_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
* If fc_pf_rjt_reason is FC_RJT_NONE then this function will have a hold
* on the ep that should be released by the caller.
*/
-static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_exch_mgr *mp,
+static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport,
+ struct fc_exch_mgr *mp,
struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
@@ -705,7 +724,7 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_exch_mgr *mp,
reject = FC_RJT_RX_ID;
goto rel;
}
- ep = fc_exch_resp(mp, fp);
+ ep = fc_exch_resp(lport, mp, fp);
if (!ep) {
reject = FC_RJT_EXCH_EST; /* XXX */
goto out;
@@ -822,7 +841,6 @@ struct fc_seq *fc_seq_start_next(struct fc_seq *sp)
struct fc_exch *ep = fc_seq_exch(sp);
spin_lock_bh(&ep->ex_lock);
- WARN_ON((ep->esb_stat & ESB_ST_COMPLETE) != 0);
sp = fc_seq_start_next_locked(sp);
spin_unlock_bh(&ep->ex_lock);
@@ -999,8 +1017,8 @@ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp,
*/
memcpy(fh->fh_s_id, rx_fh->fh_d_id, 3);
memcpy(fh->fh_d_id, rx_fh->fh_s_id, 3);
- fh->fh_ox_id = rx_fh->fh_rx_id;
- fh->fh_rx_id = rx_fh->fh_ox_id;
+ fh->fh_ox_id = rx_fh->fh_ox_id;
+ fh->fh_rx_id = rx_fh->fh_rx_id;
fh->fh_seq_cnt = rx_fh->fh_seq_cnt;
fh->fh_r_ctl = FC_RCTL_BA_RJT;
fh->fh_type = FC_TYPE_BLS;
@@ -1097,7 +1115,7 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
enum fc_pf_rjt_reason reject;
fr_seq(fp) = NULL;
- reject = fc_seq_lookup_recip(mp, fp);
+ reject = fc_seq_lookup_recip(lp, mp, fp);
if (reject == FC_RJT_NONE) {
sp = fr_seq(fp); /* sequence will be held */
ep = fc_seq_exch(sp);
@@ -1123,7 +1141,7 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp,
lp->tt.lport_recv(lp, sp, fp);
fc_exch_release(ep); /* release from lookup */
} else {
- FC_EM_DBG(mp, "exch/seq lookup failed: reject %x\n", reject);
+ FC_LPORT_DBG(lp, "exch/seq lookup failed: reject %x\n", reject);
fc_frame_free(fp);
}
}
@@ -1193,7 +1211,7 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
WARN_ON(fc_seq_exch(sp) != ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
- fc_exch_mgr_delete_ep(ep);
+ fc_exch_delete(ep);
}
/*
@@ -1229,13 +1247,12 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)
struct fc_seq *sp;
sp = fc_seq_lookup_orig(mp, fp); /* doesn't hold sequence */
- if (!sp) {
+
+ if (!sp)
atomic_inc(&mp->stats.xid_not_found);
- FC_EM_DBG(mp, "seq lookup failed\n");
- } else {
+ else
atomic_inc(&mp->stats.non_bls_resp);
- FC_EM_DBG(mp, "non-BLS response to sequence");
- }
+
fc_frame_free(fp);
}
@@ -1304,7 +1321,7 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
- fc_exch_mgr_delete_ep(ep);
+ fc_exch_delete(ep);
if (resp)
resp(sp, fp, ex_resp_arg);
@@ -1447,44 +1464,77 @@ static void fc_exch_reset(struct fc_exch *ep)
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
- fc_exch_mgr_delete_ep(ep);
+ fc_exch_delete(ep);
if (resp)
resp(sp, ERR_PTR(-FC_EX_CLOSED), arg);
}
-/*
- * Reset an exchange manager, releasing all sequences and exchanges.
- * If sid is non-zero, reset only exchanges we source from that FID.
- * If did is non-zero, reset only exchanges destined to that FID.
+/**
+ * fc_exch_pool_reset() - Resets an per cpu exches pool.
+ * @lport: ptr to the local port
+ * @pool: ptr to the per cpu exches pool
+ * @sid: source FC ID
+ * @did: destination FC ID
+ *
+ * Resets an per cpu exches pool, releasing its all sequences
+ * and exchanges. If sid is non-zero, then reset only exchanges
+ * we sourced from that FID. If did is non-zero, reset only
+ * exchanges destined to that FID.
*/
-void fc_exch_mgr_reset(struct fc_lport *lp, u32 sid, u32 did)
+static void fc_exch_pool_reset(struct fc_lport *lport,
+ struct fc_exch_pool *pool,
+ u32 sid, u32 did)
{
struct fc_exch *ep;
struct fc_exch *next;
- struct fc_exch_mgr *mp = lp->emp;
- spin_lock_bh(&mp->em_lock);
+ spin_lock_bh(&pool->lock);
restart:
- list_for_each_entry_safe(ep, next, &mp->ex_list, ex_list) {
- if ((sid == 0 || sid == ep->sid) &&
+ list_for_each_entry_safe(ep, next, &pool->ex_list, ex_list) {
+ if ((lport == ep->lp) &&
+ (sid == 0 || sid == ep->sid) &&
(did == 0 || did == ep->did)) {
fc_exch_hold(ep);
- spin_unlock_bh(&mp->em_lock);
+ spin_unlock_bh(&pool->lock);
fc_exch_reset(ep);
fc_exch_release(ep);
- spin_lock_bh(&mp->em_lock);
+ spin_lock_bh(&pool->lock);
/*
- * must restart loop incase while lock was down
- * multiple eps were released.
+ * must restart loop incase while lock
+ * was down multiple eps were released.
*/
goto restart;
}
}
- spin_unlock_bh(&mp->em_lock);
+ spin_unlock_bh(&pool->lock);
+}
+
+/**
+ * fc_exch_mgr_reset() - Resets all EMs of a lport
+ * @lport: ptr to the local port
+ * @sid: source FC ID
+ * @did: destination FC ID
+ *
+ * Reset all EMs of a lport, releasing its all sequences and
+ * exchanges. If sid is non-zero, then reset only exchanges
+ * we sourced from that FID. If did is non-zero, reset only
+ * exchanges destined to that FID.
+ */
+void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did)
+{
+ struct fc_exch_mgr_anchor *ema;
+ unsigned int cpu;
+
+ list_for_each_entry(ema, &lport->ema_list, ema_list) {
+ for_each_possible_cpu(cpu)
+ fc_exch_pool_reset(lport,
+ per_cpu_ptr(ema->mp->pool, cpu),
+ sid, did);
+ }
}
EXPORT_SYMBOL(fc_exch_mgr_reset);
@@ -1730,85 +1780,129 @@ reject:
fc_frame_free(fp);
}
+struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport,
+ struct fc_exch_mgr *mp,
+ bool (*match)(struct fc_frame *))
+{
+ struct fc_exch_mgr_anchor *ema;
+
+ ema = kmalloc(sizeof(*ema), GFP_ATOMIC);
+ if (!ema)
+ return ema;
+
+ ema->mp = mp;
+ ema->match = match;
+ /* add EM anchor to EM anchors list */
+ list_add_tail(&ema->ema_list, &lport->ema_list);
+ kref_get(&mp->kref);
+ return ema;
+}
+EXPORT_SYMBOL(fc_exch_mgr_add);
+
+static void fc_exch_mgr_destroy(struct kref *kref)
+{
+ struct fc_exch_mgr *mp = container_of(kref, struct fc_exch_mgr, kref);
+
+ mempool_destroy(mp->ep_pool);
+ free_percpu(mp->pool);
+ kfree(mp);
+}
+
+void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema)
+{
+ /* remove EM anchor from EM anchors list */
+ list_del(&ema->ema_list);
+ kref_put(&ema->mp->kref, fc_exch_mgr_destroy);
+ kfree(ema);
+}
+EXPORT_SYMBOL(fc_exch_mgr_del);
+
struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
enum fc_class class,
- u16 min_xid, u16 max_xid)
+ u16 min_xid, u16 max_xid,
+ bool (*match)(struct fc_frame *))
{
struct fc_exch_mgr *mp;
- size_t len;
+ u16 pool_exch_range;
+ size_t pool_size;
+ unsigned int cpu;
+ struct fc_exch_pool *pool;
- if (max_xid <= min_xid || min_xid == 0 || max_xid == FC_XID_UNKNOWN) {
+ if (max_xid <= min_xid || max_xid == FC_XID_UNKNOWN ||
+ (min_xid & fc_cpu_mask) != 0) {
FC_LPORT_DBG(lp, "Invalid min_xid 0x:%x and max_xid 0x:%x\n",
min_xid, max_xid);
return NULL;
}
/*
- * Memory need for EM
+ * allocate memory for EM
*/
-#define xid_ok(i, m1, m2) (((i) >= (m1)) && ((i) <= (m2)))
- len = (max_xid - min_xid + 1) * (sizeof(struct fc_exch *));
- len += sizeof(struct fc_exch_mgr);
-
- mp = kzalloc(len, GFP_ATOMIC);
+ mp = kzalloc(sizeof(struct fc_exch_mgr), GFP_ATOMIC);
if (!mp)
return NULL;
mp->class = class;
- mp->total_exches = 0;
- mp->exches = (struct fc_exch **)(mp + 1);
- mp->lp = lp;
/* adjust em exch xid range for offload */
mp->min_xid = min_xid;
mp->max_xid = max_xid;
- mp->last_xid = min_xid - 1;
- mp->max_read = 0;
- mp->last_read = 0;
- if (lp->lro_enabled && xid_ok(lp->lro_xid, min_xid, max_xid)) {
- mp->max_read = lp->lro_xid;
- mp->last_read = min_xid - 1;
- mp->last_xid = mp->max_read;
- } else {
- /* disable lro if no xid control over read */
- lp->lro_enabled = 0;
- }
-
- INIT_LIST_HEAD(&mp->ex_list);
- spin_lock_init(&mp->em_lock);
mp->ep_pool = mempool_create_slab_pool(2, fc_em_cachep);
if (!mp->ep_pool)
goto free_mp;
+ /*
+ * Setup per cpu exch pool with entire exchange id range equally
+ * divided across all cpus. The exch pointers array memory is
+ * allocated for exch range per pool.
+ */
+ pool_exch_range = (mp->max_xid - mp->min_xid + 1) / (fc_cpu_mask + 1);
+ mp->pool_max_index = pool_exch_range - 1;
+
+ /*
+ * Allocate and initialize per cpu exch pool
+ */
+ pool_size = sizeof(*pool) + pool_exch_range * sizeof(struct fc_exch *);
+ mp->pool = __alloc_percpu(pool_size, __alignof__(struct fc_exch_pool));
+ if (!mp->pool)
+ goto free_mempool;
+ for_each_possible_cpu(cpu) {
+ pool = per_cpu_ptr(mp->pool, cpu);
+ spin_lock_init(&pool->lock);
+ INIT_LIST_HEAD(&pool->ex_list);
+ }
+
+ kref_init(&mp->kref);
+ if (!fc_exch_mgr_add(lp, mp, match)) {
+ free_percpu(mp->pool);
+ goto free_mempool;
+ }
+
+ /*
+ * Above kref_init() sets mp->kref to 1 and then
+ * call to fc_exch_mgr_add incremented mp->kref again,
+ * so adjust that extra increment.
+ */
+ kref_put(&mp->kref, fc_exch_mgr_destroy);
return mp;
+free_mempool:
+ mempool_destroy(mp->ep_pool);
free_mp:
kfree(mp);
return NULL;
}
EXPORT_SYMBOL(fc_exch_mgr_alloc);
-void fc_exch_mgr_free(struct fc_exch_mgr *mp)
+void fc_exch_mgr_free(struct fc_lport *lport)
{
- WARN_ON(!mp);
- /*
- * The total exch count must be zero
- * before freeing exchange manager.
- */
- WARN_ON(mp->total_exches != 0);
- mempool_destroy(mp->ep_pool);
- kfree(mp);
+ struct fc_exch_mgr_anchor *ema, *next;
+
+ list_for_each_entry_safe(ema, next, &lport->ema_list, ema_list)
+ fc_exch_mgr_del(ema);
}
EXPORT_SYMBOL(fc_exch_mgr_free);
-struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp)
-{
- if (!lp || !lp->emp)
- return NULL;
-
- return fc_exch_alloc(lp->emp, fp, 0);
-}
-EXPORT_SYMBOL(fc_exch_get);
struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
struct fc_frame *fp,
@@ -1823,7 +1917,7 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
struct fc_frame_header *fh;
int rc = 1;
- ep = lp->tt.exch_get(lp, fp);
+ ep = fc_exch_alloc(lp, fp);
if (!ep) {
fc_frame_free(fp);
return NULL;
@@ -1843,7 +1937,8 @@ struct fc_seq *fc_exch_seq_send(struct fc_lport *lp,
fc_exch_setup_hdr(ep, fp, ep->f_ctl);
sp->cnt++;
- fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
+ if (ep->xid <= lp->lro_xid)
+ fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
if (unlikely(lp->tt.frame_send(lp, fp)))
goto err;
@@ -1860,7 +1955,7 @@ err:
rc = fc_exch_done_locked(ep);
spin_unlock_bh(&ep->ex_lock);
if (!rc)
- fc_exch_mgr_delete_ep(ep);
+ fc_exch_delete(ep);
return NULL;
}
EXPORT_SYMBOL(fc_exch_seq_send);
@@ -1868,24 +1963,44 @@ EXPORT_SYMBOL(fc_exch_seq_send);
/*
* Receive a frame
*/
-void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
- struct fc_frame *fp)
+void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp)
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
- u32 f_ctl;
+ struct fc_exch_mgr_anchor *ema;
+ u32 f_ctl, found = 0;
+ u16 oxid;
/* lport lock ? */
- if (!lp || !mp || (lp->state == LPORT_ST_NONE)) {
+ if (!lp || lp->state == LPORT_ST_DISABLED) {
FC_LPORT_DBG(lp, "Receiving frames for an lport that "
"has not been initialized correctly\n");
fc_frame_free(fp);
return;
}
+ f_ctl = ntoh24(fh->fh_f_ctl);
+ oxid = ntohs(fh->fh_ox_id);
+ if (f_ctl & FC_FC_EX_CTX) {
+ list_for_each_entry(ema, &lp->ema_list, ema_list) {
+ if ((oxid >= ema->mp->min_xid) &&
+ (oxid <= ema->mp->max_xid)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ FC_LPORT_DBG(lp, "Received response for out "
+ "of range oxid:%hx\n", oxid);
+ fc_frame_free(fp);
+ return;
+ }
+ } else
+ ema = list_entry(lp->ema_list.prev, typeof(*ema), ema_list);
+
/*
* If frame is marked invalid, just drop it.
*/
- f_ctl = ntoh24(fh->fh_f_ctl);
switch (fr_eof(fp)) {
case FC_EOF_T:
if (f_ctl & FC_FC_END_SEQ)
@@ -1893,34 +2008,24 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
/* fall through */
case FC_EOF_N:
if (fh->fh_type == FC_TYPE_BLS)
- fc_exch_recv_bls(mp, fp);
+ fc_exch_recv_bls(ema->mp, fp);
else if ((f_ctl & (FC_FC_EX_CTX | FC_FC_SEQ_CTX)) ==
FC_FC_EX_CTX)
- fc_exch_recv_seq_resp(mp, fp);
+ fc_exch_recv_seq_resp(ema->mp, fp);
else if (f_ctl & FC_FC_SEQ_CTX)
- fc_exch_recv_resp(mp, fp);
+ fc_exch_recv_resp(ema->mp, fp);
else
- fc_exch_recv_req(lp, mp, fp);
+ fc_exch_recv_req(lp, ema->mp, fp);
break;
default:
- FC_EM_DBG(mp, "dropping invalid frame (eof %x)", fr_eof(fp));
+ FC_LPORT_DBG(lp, "dropping invalid frame (eof %x)", fr_eof(fp));
fc_frame_free(fp);
- break;
}
}
EXPORT_SYMBOL(fc_exch_recv);
int fc_exch_init(struct fc_lport *lp)
{
- if (!lp->tt.exch_get) {
- /*
- * exch_put() should be NULL if
- * exch_get() is NULL
- */
- WARN_ON(lp->tt.exch_put);
- lp->tt.exch_get = fc_exch_get;
- }
-
if (!lp->tt.seq_start_next)
lp->tt.seq_start_next = fc_seq_start_next;
@@ -1942,6 +2047,28 @@ int fc_exch_init(struct fc_lport *lp)
if (!lp->tt.seq_exch_abort)
lp->tt.seq_exch_abort = fc_seq_exch_abort;
+ /*
+ * Initialize fc_cpu_mask and fc_cpu_order. The
+ * fc_cpu_mask is set for nr_cpu_ids rounded up
+ * to order of 2's * power and order is stored
+ * in fc_cpu_order as this is later required in
+ * mapping between an exch id and exch array index
+ * in per cpu exch pool.
+ *
+ * This round up is required to align fc_cpu_mask
+ * to exchange id's lower bits such that all incoming
+ * frames of an exchange gets delivered to the same
+ * cpu on which exchange originated by simple bitwise
+ * AND operation between fc_cpu_mask and exchange id.
+ */
+ fc_cpu_mask = 1;
+ fc_cpu_order = 0;
+ while (fc_cpu_mask < nr_cpu_ids) {
+ fc_cpu_mask <<= 1;
+ fc_cpu_order++;
+ }
+ fc_cpu_mask--;
+
return 0;
}
EXPORT_SYMBOL(fc_exch_init);
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index e303e0d12c4..59a4408b27b 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -507,33 +507,6 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
f_ctl = FC_FC_REL_OFF;
WARN_ON(!seq);
- /*
- * If a get_page()/put_page() will fail, don't use sg lists
- * in the fc_frame structure.
- *
- * The put_page() may be long after the I/O has completed
- * in the case of FCoE, since the network driver does it
- * via free_skb(). See the test in free_pages_check().
- *
- * Test this case with 'dd </dev/zero >/dev/st0 bs=64k'.
- */
- if (using_sg) {
- for (sg = scsi_sglist(sc); sg; sg = sg_next(sg)) {
- if (page_count(sg_page(sg)) == 0 ||
- (sg_page(sg)->flags & (1 << PG_lru |
- 1 << PG_private |
- 1 << PG_locked |
- 1 << PG_active |
- 1 << PG_slab |
- 1 << PG_swapcache |
- 1 << PG_writeback |
- 1 << PG_reserved |
- 1 << PG_buddy))) {
- using_sg = 0;
- break;
- }
- }
- }
sg = scsi_sglist(sc);
while (remaining > 0 && sg) {
@@ -569,8 +542,6 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
}
sg_bytes = min(tlen, sg->length - offset);
if (using_sg) {
- WARN_ON(skb_shinfo(fp_skb(fp))->nr_frags >
- FC_FRAME_SG_LEN);
get_page(sg_page(sg));
skb_fill_page_desc(fp_skb(fp),
skb_shinfo(fp_skb(fp))->nr_frags,
@@ -1337,7 +1308,7 @@ static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rport->port_id,
fc_host_port_id(rp->local_port->host), FC_TYPE_ELS,
FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0);
- if (lp->tt.elsct_send(lp, rport, fp, ELS_REC, fc_fcp_rec_resp,
+ if (lp->tt.elsct_send(lp, rport->port_id, fp, ELS_REC, fc_fcp_rec_resp,
fsp, jiffies_to_msecs(FC_SCSI_REC_TOV))) {
fc_fcp_pkt_hold(fsp); /* hold while REC outstanding */
return;
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index 745fa5555d6..bd2f7719744 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -113,7 +113,7 @@ static void fc_lport_enter_ready(struct fc_lport *);
static void fc_lport_enter_logo(struct fc_lport *);
static const char *fc_lport_state_names[] = {
- [LPORT_ST_NONE] = "none",
+ [LPORT_ST_DISABLED] = "disabled",
[LPORT_ST_FLOGI] = "FLOGI",
[LPORT_ST_DNS] = "dNS",
[LPORT_ST_RPN_ID] = "RPN_ID",
@@ -133,57 +133,44 @@ static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)
/**
* fc_lport_rport_callback() - Event handler for rport events
* @lport: The lport which is receiving the event
- * @rport: The rport which the event has occured on
+ * @rdata: private remote port data
* @event: The event that occured
*
* Locking Note: The rport lock should not be held when calling
* this function.
*/
static void fc_lport_rport_callback(struct fc_lport *lport,
- struct fc_rport *rport,
+ struct fc_rport_priv *rdata,
enum fc_rport_event event)
{
FC_LPORT_DBG(lport, "Received a %d event for port (%6x)\n", event,
- rport->port_id);
+ rdata->ids.port_id);
+ mutex_lock(&lport->lp_mutex);
switch (event) {
- case RPORT_EV_CREATED:
- if (rport->port_id == FC_FID_DIR_SERV) {
- mutex_lock(&lport->lp_mutex);
- if (lport->state == LPORT_ST_DNS) {
- lport->dns_rp = rport;
- fc_lport_enter_rpn_id(lport);
- } else {
- FC_LPORT_DBG(lport, "Received an CREATED event "
- "on port (%6x) for the directory "
- "server, but the lport is not "
- "in the DNS state, it's in the "
- "%d state", rport->port_id,
- lport->state);
- lport->tt.rport_logoff(rport);
- }
- mutex_unlock(&lport->lp_mutex);
- } else
- FC_LPORT_DBG(lport, "Received an event for port (%6x) "
- "which is not the directory server\n",
- rport->port_id);
+ case RPORT_EV_READY:
+ if (lport->state == LPORT_ST_DNS) {
+ lport->dns_rp = rdata;
+ fc_lport_enter_rpn_id(lport);
+ } else {
+ FC_LPORT_DBG(lport, "Received an READY event "
+ "on port (%6x) for the directory "
+ "server, but the lport is not "
+ "in the DNS state, it's in the "
+ "%d state", rdata->ids.port_id,
+ lport->state);
+ lport->tt.rport_logoff(rdata);
+ }
break;
case RPORT_EV_LOGO:
case RPORT_EV_FAILED:
case RPORT_EV_STOP:
- if (rport->port_id == FC_FID_DIR_SERV) {
- mutex_lock(&lport->lp_mutex);
- lport->dns_rp = NULL;
- mutex_unlock(&lport->lp_mutex);
-
- } else
- FC_LPORT_DBG(lport, "Received an event for port (%6x) "
- "which is not the directory server\n",
- rport->port_id);
+ lport->dns_rp = NULL;
break;
case RPORT_EV_NONE:
break;
}
+ mutex_unlock(&lport->lp_mutex);
}
/**
@@ -211,20 +198,13 @@ static void fc_lport_ptp_setup(struct fc_lport *lport,
u32 remote_fid, u64 remote_wwpn,
u64 remote_wwnn)
{
- struct fc_disc_port dp;
-
- dp.lp = lport;
- dp.ids.port_id = remote_fid;
- dp.ids.port_name = remote_wwpn;
- dp.ids.node_name = remote_wwnn;
- dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
-
- if (lport->ptp_rp) {
+ mutex_lock(&lport->disc.disc_mutex);
+ if (lport->ptp_rp)
lport->tt.rport_logoff(lport->ptp_rp);
- lport->ptp_rp = NULL;
- }
-
- lport->ptp_rp = lport->tt.rport_create(&dp);
+ lport->ptp_rp = lport->tt.rport_create(lport, remote_fid);
+ lport->ptp_rp->ids.port_name = remote_wwpn;
+ lport->ptp_rp->ids.node_name = remote_wwnn;
+ mutex_unlock(&lport->disc.disc_mutex);
lport->tt.rport_login(lport->ptp_rp);
@@ -472,56 +452,6 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp,
}
/**
- * fc_lport_recv_adisc_req() - Handle received Address Discovery Request
- * @lport: Fibre Channel local port recieving the ADISC
- * @sp: current sequence in the ADISC exchange
- * @fp: ADISC request frame
- *
- * Locking Note: The lport lock is expected to be held before calling
- * this function.
- */
-static void fc_lport_recv_adisc_req(struct fc_seq *sp, struct fc_frame *in_fp,
- struct fc_lport *lport)
-{
- struct fc_frame *fp;
- struct fc_exch *ep = fc_seq_exch(sp);
- struct fc_els_adisc *req, *rp;
- struct fc_seq_els_data rjt_data;
- size_t len;
- u32 f_ctl;
-
- FC_LPORT_DBG(lport, "Received ADISC request while in state %s\n",
- fc_lport_state(lport));
-
- req = fc_frame_payload_get(in_fp, sizeof(*req));
- if (!req) {
- rjt_data.fp = NULL;
- rjt_data.reason = ELS_RJT_LOGIC;
- rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
- } else {
- len = sizeof(*rp);
- fp = fc_frame_alloc(lport, len);
- if (fp) {
- rp = fc_frame_payload_get(fp, len);
- memset(rp, 0, len);
- rp->adisc_cmd = ELS_LS_ACC;
- rp->adisc_wwpn = htonll(lport->wwpn);
- rp->adisc_wwnn = htonll(lport->wwnn);
- hton24(rp->adisc_port_id,
- fc_host_port_id(lport->host));
- sp = lport->tt.seq_start_next(sp);
- f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
- f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
- fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
- FC_TYPE_ELS, f_ctl, 0);
- lport->tt.seq_send(lport, sp, fp);
- }
- }
- fc_frame_free(in_fp);
-}
-
-/**
* fc_lport_recv_logo_req() - Handle received fabric LOGO request
* @lport: Fibre Channel local port recieving the LOGO
* @sp: current sequence in the LOGO exchange
@@ -550,7 +480,7 @@ int fc_fabric_login(struct fc_lport *lport)
int rc = -1;
mutex_lock(&lport->lp_mutex);
- if (lport->state == LPORT_ST_NONE) {
+ if (lport->state == LPORT_ST_DISABLED) {
fc_lport_enter_reset(lport);
rc = 0;
}
@@ -637,12 +567,13 @@ EXPORT_SYMBOL(fc_fabric_logoff);
int fc_lport_destroy(struct fc_lport *lport)
{
mutex_lock(&lport->lp_mutex);
- lport->state = LPORT_ST_NONE;
+ lport->state = LPORT_ST_DISABLED;
lport->link_up = 0;
lport->tt.frame_send = fc_frame_drop;
mutex_unlock(&lport->lp_mutex);
lport->tt.fcp_abort_io(lport);
+ lport->tt.disc_stop_final(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
return 0;
}
@@ -722,7 +653,8 @@ static void fc_lport_enter_ready(struct fc_lport *lport)
fc_lport_state_enter(lport, LPORT_ST_READY);
- lport->tt.disc_start(fc_lport_disc_callback, lport);
+ if (!lport->ptp_rp)
+ lport->tt.disc_start(fc_lport_disc_callback, lport);
}
/**
@@ -808,8 +740,6 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in,
fc_lport_ptp_setup(lport, remote_fid, remote_wwpn,
get_unaligned_be64(&flp->fl_wwnn));
- lport->tt.disc_start(fc_lport_disc_callback, lport);
-
out:
sp = fr_seq(rx_fp);
fc_frame_free(rx_fp);
@@ -832,10 +762,6 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
{
struct fc_frame_header *fh = fc_frame_header_get(fp);
void (*recv) (struct fc_seq *, struct fc_frame *, struct fc_lport *);
- struct fc_rport *rport;
- u32 s_id;
- u32 d_id;
- struct fc_seq_els_data rjt_data;
mutex_lock(&lport->lp_mutex);
@@ -844,11 +770,14 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
* RSCN here. These don't require a session.
* Even if we had a session, it might not be ready.
*/
- if (fh->fh_type == FC_TYPE_ELS && fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
+ if (!lport->link_up)
+ fc_frame_free(fp);
+ else if (fh->fh_type == FC_TYPE_ELS &&
+ fh->fh_r_ctl == FC_RCTL_ELS_REQ) {
/*
* Check opcode.
*/
- recv = NULL;
+ recv = lport->tt.rport_recv_req;
switch (fc_frame_payload_op(fp)) {
case ELS_FLOGI:
recv = fc_lport_recv_flogi_req;
@@ -870,34 +799,9 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp,
case ELS_RNID:
recv = fc_lport_recv_rnid_req;
break;
- case ELS_ADISC:
- recv = fc_lport_recv_adisc_req;
- break;
}
- if (recv)
- recv(sp, fp, lport);
- else {
- /*
- * Find session.
- * If this is a new incoming PLOGI, we won't find it.
- */
- s_id = ntoh24(fh->fh_s_id);
- d_id = ntoh24(fh->fh_d_id);
-
- rport = lport->tt.rport_lookup(lport, s_id);
- if (rport)
- lport->tt.rport_recv_req(sp, fp, rport);
- else {
- rjt_data.fp = NULL;
- rjt_data.reason = ELS_RJT_UNAB;
- rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(sp,
- ELS_LS_RJT,
- &rjt_data);
- fc_frame_free(fp);
- }
- }
+ recv(sp, fp, lport);
} else {
FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n",
fr_eof(fp));
@@ -930,38 +834,61 @@ int fc_lport_reset(struct fc_lport *lport)
EXPORT_SYMBOL(fc_lport_reset);
/**
- * fc_rport_enter_reset() - Reset the local port
+ * fc_lport_reset_locked() - Reset the local port
* @lport: Fibre Channel local port to be reset
*
* Locking Note: The lport lock is expected to be held before calling
* this routine.
*/
-static void fc_lport_enter_reset(struct fc_lport *lport)
+static void fc_lport_reset_locked(struct fc_lport *lport)
{
- FC_LPORT_DBG(lport, "Entered RESET state from %s state\n",
- fc_lport_state(lport));
-
- fc_lport_state_enter(lport, LPORT_ST_RESET);
-
if (lport->dns_rp)
lport->tt.rport_logoff(lport->dns_rp);
- if (lport->ptp_rp) {
- lport->tt.rport_logoff(lport->ptp_rp);
- lport->ptp_rp = NULL;
- }
+ lport->ptp_rp = NULL;
lport->tt.disc_stop(lport);
lport->tt.exch_mgr_reset(lport, 0, 0);
fc_host_fabric_name(lport->host) = 0;
fc_host_port_id(lport->host) = 0;
+}
+/**
+ * fc_lport_enter_reset() - Reset the local port
+ * @lport: Fibre Channel local port to be reset
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_reset(struct fc_lport *lport)
+{
+ FC_LPORT_DBG(lport, "Entered RESET state from %s state\n",
+ fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_RESET);
+ fc_lport_reset_locked(lport);
if (lport->link_up)
fc_lport_enter_flogi(lport);
}
/**
+ * fc_lport_enter_disabled() - disable the local port
+ * @lport: Fibre Channel local port to be reset
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_disabled(struct fc_lport *lport)
+{
+ FC_LPORT_DBG(lport, "Entered disabled state from %s state\n",
+ fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_DISABLED);
+ fc_lport_reset_locked(lport);
+}
+
+/**
* fc_lport_error() - Handler for any errors
* @lport: The fc_lport object
* @fp: The frame pointer
@@ -992,7 +919,7 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
schedule_delayed_work(&lport->retry_work, delay);
} else {
switch (lport->state) {
- case LPORT_ST_NONE:
+ case LPORT_ST_DISABLED:
case LPORT_ST_READY:
case LPORT_ST_RESET:
case LPORT_ST_RPN_ID:
@@ -1026,13 +953,13 @@ static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_frame_header *fh;
struct fc_ct_hdr *ct;
+ FC_LPORT_DBG(lport, "Received a RFT_ID %s\n", fc_els_resp_type(fp));
+
if (fp == ERR_PTR(-FC_EX_CLOSED))
return;
mutex_lock(&lport->lp_mutex);
- FC_LPORT_DBG(lport, "Received a RFT_ID response\n");
-
if (lport->state != LPORT_ST_RFT_ID) {
FC_LPORT_DBG(lport, "Received a RFT_ID response, but in state "
"%s\n", fc_lport_state(lport));
@@ -1080,13 +1007,13 @@ static void fc_lport_rpn_id_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_frame_header *fh;
struct fc_ct_hdr *ct;
+ FC_LPORT_DBG(lport, "Received a RPN_ID %s\n", fc_els_resp_type(fp));
+
if (fp == ERR_PTR(-FC_EX_CLOSED))
return;
mutex_lock(&lport->lp_mutex);
- FC_LPORT_DBG(lport, "Received a RPN_ID response\n");
-
if (lport->state != LPORT_ST_RPN_ID) {
FC_LPORT_DBG(lport, "Received a RPN_ID response, but in state "
"%s\n", fc_lport_state(lport));
@@ -1132,13 +1059,13 @@ static void fc_lport_scr_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_lport *lport = lp_arg;
u8 op;
+ FC_LPORT_DBG(lport, "Received a SCR %s\n", fc_els_resp_type(fp));
+
if (fp == ERR_PTR(-FC_EX_CLOSED))
return;
mutex_lock(&lport->lp_mutex);
- FC_LPORT_DBG(lport, "Received a SCR response\n");
-
if (lport->state != LPORT_ST_SCR) {
FC_LPORT_DBG(lport, "Received a SCR response, but in state "
"%s\n", fc_lport_state(lport));
@@ -1186,7 +1113,7 @@ static void fc_lport_enter_scr(struct fc_lport *lport)
return;
}
- if (!lport->tt.elsct_send(lport, NULL, fp, ELS_SCR,
+ if (!lport->tt.elsct_send(lport, FC_FID_FCTRL, fp, ELS_SCR,
fc_lport_scr_resp, lport, lport->e_d_tov))
fc_lport_error(lport, fp);
}
@@ -1227,7 +1154,7 @@ static void fc_lport_enter_rft_id(struct fc_lport *lport)
return;
}
- if (!lport->tt.elsct_send(lport, NULL, fp, FC_NS_RFT_ID,
+ if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RFT_ID,
fc_lport_rft_id_resp,
lport, lport->e_d_tov))
fc_lport_error(lport, fp);
@@ -1256,7 +1183,7 @@ static void fc_lport_enter_rpn_id(struct fc_lport *lport)
return;
}
- if (!lport->tt.elsct_send(lport, NULL, fp, FC_NS_RPN_ID,
+ if (!lport->tt.elsct_send(lport, FC_FID_DIR_SERV, fp, FC_NS_RPN_ID,
fc_lport_rpn_id_resp,
lport, lport->e_d_tov))
fc_lport_error(lport, fp);
@@ -1275,28 +1202,21 @@ static struct fc_rport_operations fc_lport_rport_ops = {
*/
static void fc_lport_enter_dns(struct fc_lport *lport)
{
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata;
- struct fc_disc_port dp;
-
- dp.ids.port_id = FC_FID_DIR_SERV;
- dp.ids.port_name = -1;
- dp.ids.node_name = -1;
- dp.ids.roles = FC_RPORT_ROLE_UNKNOWN;
- dp.lp = lport;
+ struct fc_rport_priv *rdata;
FC_LPORT_DBG(lport, "Entered DNS state from %s state\n",
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_DNS);
- rport = lport->tt.rport_create(&dp);
- if (!rport)
+ mutex_lock(&lport->disc.disc_mutex);
+ rdata = lport->tt.rport_create(lport, FC_FID_DIR_SERV);
+ mutex_unlock(&lport->disc.disc_mutex);
+ if (!rdata)
goto err;
- rdata = rport->dd_data;
rdata->ops = &fc_lport_rport_ops;
- lport->tt.rport_login(rport);
+ lport->tt.rport_login(rdata);
return;
err:
@@ -1316,7 +1236,7 @@ static void fc_lport_timeout(struct work_struct *work)
mutex_lock(&lport->lp_mutex);
switch (lport->state) {
- case LPORT_ST_NONE:
+ case LPORT_ST_DISABLED:
case LPORT_ST_READY:
case LPORT_ST_RESET:
WARN_ON(1);
@@ -1360,13 +1280,13 @@ static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
struct fc_lport *lport = lp_arg;
u8 op;
+ FC_LPORT_DBG(lport, "Received a LOGO %s\n", fc_els_resp_type(fp));
+
if (fp == ERR_PTR(-FC_EX_CLOSED))
return;
mutex_lock(&lport->lp_mutex);
- FC_LPORT_DBG(lport, "Received a LOGO response\n");
-
if (lport->state != LPORT_ST_LOGO) {
FC_LPORT_DBG(lport, "Received a LOGO response, but in state "
"%s\n", fc_lport_state(lport));
@@ -1382,7 +1302,7 @@ static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC)
- fc_lport_enter_reset(lport);
+ fc_lport_enter_disabled(lport);
else
fc_lport_error(lport, fp);
@@ -1415,8 +1335,8 @@ static void fc_lport_enter_logo(struct fc_lport *lport)
return;
}
- if (!lport->tt.elsct_send(lport, NULL, fp, ELS_LOGO, fc_lport_logo_resp,
- lport, lport->e_d_tov))
+ if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_LOGO,
+ fc_lport_logo_resp, lport, lport->e_d_tov))
fc_lport_error(lport, fp);
}
@@ -1442,13 +1362,13 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
unsigned int e_d_tov;
u16 mfs;
+ FC_LPORT_DBG(lport, "Received a FLOGI %s\n", fc_els_resp_type(fp));
+
if (fp == ERR_PTR(-FC_EX_CLOSED))
return;
mutex_lock(&lport->lp_mutex);
- FC_LPORT_DBG(lport, "Received a FLOGI response\n");
-
if (lport->state != LPORT_ST_FLOGI) {
FC_LPORT_DBG(lport, "Received a FLOGI response, but in state "
"%s\n", fc_lport_state(lport));
@@ -1501,14 +1421,6 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,
fc_lport_enter_dns(lport);
}
}
-
- if (flp) {
- csp_flags = ntohs(flp->fl_csp.sp_features);
- if ((csp_flags & FC_SP_FT_FPORT) == 0) {
- lport->tt.disc_start(fc_lport_disc_callback,
- lport);
- }
- }
} else {
FC_LPORT_DBG(lport, "Bad FLOGI response\n");
}
@@ -1539,7 +1451,7 @@ void fc_lport_enter_flogi(struct fc_lport *lport)
if (!fp)
return fc_lport_error(lport, fp);
- if (!lport->tt.elsct_send(lport, NULL, fp, ELS_FLOGI,
+ if (!lport->tt.elsct_send(lport, FC_FID_FLOGI, fp, ELS_FLOGI,
fc_lport_flogi_resp, lport, lport->e_d_tov))
fc_lport_error(lport, fp);
}
@@ -1550,7 +1462,7 @@ int fc_lport_config(struct fc_lport *lport)
INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout);
mutex_init(&lport->lp_mutex);
- fc_lport_state_enter(lport, LPORT_ST_NONE);
+ fc_lport_state_enter(lport, LPORT_ST_DISABLED);
fc_lport_add_fc4_type(lport, FC_TYPE_FCP);
fc_lport_add_fc4_type(lport, FC_TYPE_CT);
@@ -1588,6 +1500,7 @@ int fc_lport_init(struct fc_lport *lport)
if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)
fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT;
+ INIT_LIST_HEAD(&lport->ema_list);
return 0;
}
EXPORT_SYMBOL(fc_lport_init);
diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c
index 7162385f52e..03ea6748e7e 100644
--- a/drivers/scsi/libfc/fc_rport.c
+++ b/drivers/scsi/libfc/fc_rport.c
@@ -57,94 +57,114 @@
struct workqueue_struct *rport_event_queue;
-static void fc_rport_enter_plogi(struct fc_rport *);
-static void fc_rport_enter_prli(struct fc_rport *);
-static void fc_rport_enter_rtv(struct fc_rport *);
-static void fc_rport_enter_ready(struct fc_rport *);
-static void fc_rport_enter_logo(struct fc_rport *);
-
-static void fc_rport_recv_plogi_req(struct fc_rport *,
+static void fc_rport_enter_plogi(struct fc_rport_priv *);
+static void fc_rport_enter_prli(struct fc_rport_priv *);
+static void fc_rport_enter_rtv(struct fc_rport_priv *);
+static void fc_rport_enter_ready(struct fc_rport_priv *);
+static void fc_rport_enter_logo(struct fc_rport_priv *);
+static void fc_rport_enter_adisc(struct fc_rport_priv *);
+
+static void fc_rport_recv_plogi_req(struct fc_lport *,
struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_prli_req(struct fc_rport *,
+static void fc_rport_recv_prli_req(struct fc_rport_priv *,
struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_prlo_req(struct fc_rport *,
+static void fc_rport_recv_prlo_req(struct fc_rport_priv *,
struct fc_seq *, struct fc_frame *);
-static void fc_rport_recv_logo_req(struct fc_rport *,
+static void fc_rport_recv_logo_req(struct fc_lport *,
struct fc_seq *, struct fc_frame *);
static void fc_rport_timeout(struct work_struct *);
-static void fc_rport_error(struct fc_rport *, struct fc_frame *);
-static void fc_rport_error_retry(struct fc_rport *, struct fc_frame *);
+static void fc_rport_error(struct fc_rport_priv *, struct fc_frame *);
+static void fc_rport_error_retry(struct fc_rport_priv *, struct fc_frame *);
static void fc_rport_work(struct work_struct *);
static const char *fc_rport_state_names[] = {
- [RPORT_ST_NONE] = "None",
[RPORT_ST_INIT] = "Init",
[RPORT_ST_PLOGI] = "PLOGI",
[RPORT_ST_PRLI] = "PRLI",
[RPORT_ST_RTV] = "RTV",
[RPORT_ST_READY] = "Ready",
[RPORT_ST_LOGO] = "LOGO",
+ [RPORT_ST_ADISC] = "ADISC",
+ [RPORT_ST_DELETE] = "Delete",
};
-static void fc_rport_rogue_destroy(struct device *dev)
+/**
+ * fc_rport_lookup() - lookup a remote port by port_id
+ * @lport: Fibre Channel host port instance
+ * @port_id: remote port port_id to match
+ */
+static struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport,
+ u32 port_id)
{
- struct fc_rport *rport = dev_to_rport(dev);
- FC_RPORT_DBG(rport, "Destroying rogue rport\n");
- kfree(rport);
+ struct fc_rport_priv *rdata;
+
+ list_for_each_entry(rdata, &lport->disc.rports, peers)
+ if (rdata->ids.port_id == port_id &&
+ rdata->rp_state != RPORT_ST_DELETE)
+ return rdata;
+ return NULL;
}
-struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *dp)
+/**
+ * fc_rport_create() - Create a new remote port
+ * @lport: The local port that the new remote port is for
+ * @port_id: The port ID for the new remote port
+ *
+ * Locking note: must be called with the disc_mutex held.
+ */
+static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,
+ u32 port_id)
{
- struct fc_rport *rport;
- struct fc_rport_libfc_priv *rdata;
- rport = kzalloc(sizeof(*rport) + sizeof(*rdata), GFP_KERNEL);
+ struct fc_rport_priv *rdata;
- if (!rport)
- return NULL;
+ rdata = lport->tt.rport_lookup(lport, port_id);
+ if (rdata)
+ return rdata;
- rdata = RPORT_TO_PRIV(rport);
+ rdata = kzalloc(sizeof(*rdata), GFP_KERNEL);
+ if (!rdata)
+ return NULL;
- rport->dd_data = rdata;
- rport->port_id = dp->ids.port_id;
- rport->port_name = dp->ids.port_name;
- rport->node_name = dp->ids.node_name;
- rport->roles = dp->ids.roles;
- rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
- /*
- * Note: all this libfc rogue rport code will be removed for
- * upstream so it fine that this is really ugly and hacky right now.
- */
- device_initialize(&rport->dev);
- rport->dev.release = fc_rport_rogue_destroy;
+ rdata->ids.node_name = -1;
+ rdata->ids.port_name = -1;
+ rdata->ids.port_id = port_id;
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ kref_init(&rdata->kref);
mutex_init(&rdata->rp_mutex);
- rdata->local_port = dp->lp;
- rdata->trans_state = FC_PORTSTATE_ROGUE;
+ rdata->local_port = lport;
rdata->rp_state = RPORT_ST_INIT;
rdata->event = RPORT_EV_NONE;
rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
- rdata->ops = NULL;
- rdata->e_d_tov = dp->lp->e_d_tov;
- rdata->r_a_tov = dp->lp->r_a_tov;
+ rdata->e_d_tov = lport->e_d_tov;
+ rdata->r_a_tov = lport->r_a_tov;
+ rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;
INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout);
INIT_WORK(&rdata->event_work, fc_rport_work);
- /*
- * For good measure, but not necessary as we should only
- * add REAL rport to the lport list.
- */
- INIT_LIST_HEAD(&rdata->peers);
+ if (port_id != FC_FID_DIR_SERV)
+ list_add(&rdata->peers, &lport->disc.rports);
+ return rdata;
+}
+
+/**
+ * fc_rport_destroy() - free a remote port after last reference is released.
+ * @kref: pointer to kref inside struct fc_rport_priv
+ */
+static void fc_rport_destroy(struct kref *kref)
+{
+ struct fc_rport_priv *rdata;
- return rport;
+ rdata = container_of(kref, struct fc_rport_priv, kref);
+ kfree(rdata);
}
/**
* fc_rport_state() - return a string for the state the rport is in
- * @rport: The rport whose state we want to get a string for
+ * @rdata: remote port private data
*/
-static const char *fc_rport_state(struct fc_rport *rport)
+static const char *fc_rport_state(struct fc_rport_priv *rdata)
{
const char *cp;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
cp = fc_rport_state_names[rdata->rp_state];
if (!cp)
@@ -191,15 +211,14 @@ static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp,
/**
* fc_rport_state_enter() - Change the rport's state
- * @rport: The rport whose state should change
+ * @rdata: The rport whose state should change
* @new: The new state of the rport
*
* Locking Note: Called with the rport lock held
*/
-static void fc_rport_state_enter(struct fc_rport *rport,
+static void fc_rport_state_enter(struct fc_rport_priv *rdata,
enum fc_rport_state new)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
if (rdata->rp_state != new)
rdata->retries = 0;
rdata->rp_state = new;
@@ -208,147 +227,187 @@ static void fc_rport_state_enter(struct fc_rport *rport,
static void fc_rport_work(struct work_struct *work)
{
u32 port_id;
- struct fc_rport_libfc_priv *rdata =
- container_of(work, struct fc_rport_libfc_priv, event_work);
+ struct fc_rport_priv *rdata =
+ container_of(work, struct fc_rport_priv, event_work);
+ struct fc_rport_libfc_priv *rp;
enum fc_rport_event event;
- enum fc_rport_trans_state trans_state;
struct fc_lport *lport = rdata->local_port;
struct fc_rport_operations *rport_ops;
- struct fc_rport *rport = PRIV_TO_RPORT(rdata);
+ struct fc_rport_identifiers ids;
+ struct fc_rport *rport;
mutex_lock(&rdata->rp_mutex);
event = rdata->event;
rport_ops = rdata->ops;
+ rport = rdata->rport;
- if (event == RPORT_EV_CREATED) {
- struct fc_rport *new_rport;
- struct fc_rport_libfc_priv *new_rdata;
- struct fc_rport_identifiers ids;
+ FC_RPORT_DBG(rdata, "work event %u\n", event);
- ids.port_id = rport->port_id;
- ids.roles = rport->roles;
- ids.port_name = rport->port_name;
- ids.node_name = rport->node_name;
+ switch (event) {
+ case RPORT_EV_READY:
+ ids = rdata->ids;
+ rdata->event = RPORT_EV_NONE;
+ kref_get(&rdata->kref);
+ mutex_unlock(&rdata->rp_mutex);
+ if (!rport)
+ rport = fc_remote_port_add(lport->host, 0, &ids);
+ if (!rport) {
+ FC_RPORT_DBG(rdata, "Failed to add the rport\n");
+ lport->tt.rport_logoff(rdata);
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
+ return;
+ }
+ mutex_lock(&rdata->rp_mutex);
+ if (rdata->rport)
+ FC_RPORT_DBG(rdata, "rport already allocated\n");
+ rdata->rport = rport;
+ rport->maxframe_size = rdata->maxframe_size;
+ rport->supported_classes = rdata->supported_classes;
+
+ rp = rport->dd_data;
+ rp->local_port = lport;
+ rp->rp_state = rdata->rp_state;
+ rp->flags = rdata->flags;
+ rp->e_d_tov = rdata->e_d_tov;
+ rp->r_a_tov = rdata->r_a_tov;
mutex_unlock(&rdata->rp_mutex);
- new_rport = fc_remote_port_add(lport->host, 0, &ids);
- if (new_rport) {
- /*
- * Switch from the rogue rport to the rport
- * returned by the FC class.
- */
- new_rport->maxframe_size = rport->maxframe_size;
-
- new_rdata = new_rport->dd_data;
- new_rdata->e_d_tov = rdata->e_d_tov;
- new_rdata->r_a_tov = rdata->r_a_tov;
- new_rdata->ops = rdata->ops;
- new_rdata->local_port = rdata->local_port;
- new_rdata->flags = FC_RP_FLAGS_REC_SUPPORTED;
- new_rdata->trans_state = FC_PORTSTATE_REAL;
- mutex_init(&new_rdata->rp_mutex);
- INIT_DELAYED_WORK(&new_rdata->retry_work,
- fc_rport_timeout);
- INIT_LIST_HEAD(&new_rdata->peers);
- INIT_WORK(&new_rdata->event_work, fc_rport_work);
-
- fc_rport_state_enter(new_rport, RPORT_ST_READY);
- } else {
- printk(KERN_WARNING "libfc: Failed to allocate "
- " memory for rport (%6x)\n", ids.port_id);
- event = RPORT_EV_FAILED;
+ if (rport_ops && rport_ops->event_callback) {
+ FC_RPORT_DBG(rdata, "callback ev %d\n", event);
+ rport_ops->event_callback(lport, rdata, event);
}
- if (rport->port_id != FC_FID_DIR_SERV)
- if (rport_ops->event_callback)
- rport_ops->event_callback(lport, rport,
- RPORT_EV_FAILED);
- put_device(&rport->dev);
- rport = new_rport;
- rdata = new_rport->dd_data;
- if (rport_ops->event_callback)
- rport_ops->event_callback(lport, rport, event);
- } else if ((event == RPORT_EV_FAILED) ||
- (event == RPORT_EV_LOGO) ||
- (event == RPORT_EV_STOP)) {
- trans_state = rdata->trans_state;
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
+ break;
+
+ case RPORT_EV_FAILED:
+ case RPORT_EV_LOGO:
+ case RPORT_EV_STOP:
+ port_id = rdata->ids.port_id;
mutex_unlock(&rdata->rp_mutex);
- if (rport_ops->event_callback)
- rport_ops->event_callback(lport, rport, event);
- if (trans_state == FC_PORTSTATE_ROGUE)
- put_device(&rport->dev);
- else {
- port_id = rport->port_id;
+
+ if (port_id != FC_FID_DIR_SERV) {
+ mutex_lock(&lport->disc.disc_mutex);
+ list_del(&rdata->peers);
+ mutex_unlock(&lport->disc.disc_mutex);
+ }
+
+ if (rport_ops && rport_ops->event_callback) {
+ FC_RPORT_DBG(rdata, "callback ev %d\n", event);
+ rport_ops->event_callback(lport, rdata, event);
+ }
+ cancel_delayed_work_sync(&rdata->retry_work);
+
+ /*
+ * Reset any outstanding exchanges before freeing rport.
+ */
+ lport->tt.exch_mgr_reset(lport, 0, port_id);
+ lport->tt.exch_mgr_reset(lport, port_id, 0);
+
+ if (rport) {
+ rp = rport->dd_data;
+ rp->rp_state = RPORT_ST_DELETE;
+ mutex_lock(&rdata->rp_mutex);
+ rdata->rport = NULL;
+ mutex_unlock(&rdata->rp_mutex);
fc_remote_port_delete(rport);
- lport->tt.exch_mgr_reset(lport, 0, port_id);
- lport->tt.exch_mgr_reset(lport, port_id, 0);
}
- } else
+ kref_put(&rdata->kref, lport->tt.rport_destroy);
+ break;
+
+ default:
mutex_unlock(&rdata->rp_mutex);
+ break;
+ }
}
/**
* fc_rport_login() - Start the remote port login state machine
- * @rport: Fibre Channel remote port
+ * @rdata: private remote port
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
* function and then unlock the rport.
+ *
+ * This indicates the intent to be logged into the remote port.
+ * If it appears we are already logged in, ADISC is used to verify
+ * the setup.
*/
-int fc_rport_login(struct fc_rport *rport)
+int fc_rport_login(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Login to port\n");
-
- fc_rport_enter_plogi(rport);
-
+ switch (rdata->rp_state) {
+ case RPORT_ST_READY:
+ FC_RPORT_DBG(rdata, "ADISC port\n");
+ fc_rport_enter_adisc(rdata);
+ break;
+ default:
+ FC_RPORT_DBG(rdata, "Login to port\n");
+ fc_rport_enter_plogi(rdata);
+ break;
+ }
mutex_unlock(&rdata->rp_mutex);
return 0;
}
/**
+ * fc_rport_enter_delete() - schedule a remote port to be deleted.
+ * @rdata: private remote port
+ * @event: event to report as the reason for deletion
+ *
+ * Locking Note: Called with the rport lock held.
+ *
+ * Allow state change into DELETE only once.
+ *
+ * Call queue_work only if there's no event already pending.
+ * Set the new event so that the old pending event will not occur.
+ * Since we have the mutex, even if fc_rport_work() is already started,
+ * it'll see the new event.
+ */
+static void fc_rport_enter_delete(struct fc_rport_priv *rdata,
+ enum fc_rport_event event)
+{
+ if (rdata->rp_state == RPORT_ST_DELETE)
+ return;
+
+ FC_RPORT_DBG(rdata, "Delete port\n");
+
+ fc_rport_state_enter(rdata, RPORT_ST_DELETE);
+
+ if (rdata->event == RPORT_EV_NONE)
+ queue_work(rport_event_queue, &rdata->event_work);
+ rdata->event = event;
+}
+
+/**
* fc_rport_logoff() - Logoff and remove an rport
- * @rport: Fibre Channel remote port to be removed
+ * @rdata: private remote port
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
* function and then unlock the rport.
*/
-int fc_rport_logoff(struct fc_rport *rport)
+int fc_rport_logoff(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Remove port\n");
+ FC_RPORT_DBG(rdata, "Remove port\n");
- if (rdata->rp_state == RPORT_ST_NONE) {
- FC_RPORT_DBG(rport, "Port in NONE state, not removing\n");
+ if (rdata->rp_state == RPORT_ST_DELETE) {
+ FC_RPORT_DBG(rdata, "Port in Delete state, not removing\n");
mutex_unlock(&rdata->rp_mutex);
goto out;
}
- fc_rport_enter_logo(rport);
+ fc_rport_enter_logo(rdata);
/*
- * Change the state to NONE so that we discard
+ * Change the state to Delete so that we discard
* the response.
*/
- fc_rport_state_enter(rport, RPORT_ST_NONE);
-
- mutex_unlock(&rdata->rp_mutex);
-
- cancel_delayed_work_sync(&rdata->retry_work);
-
- mutex_lock(&rdata->rp_mutex);
-
- rdata->event = RPORT_EV_STOP;
- queue_work(rport_event_queue, &rdata->event_work);
-
+ fc_rport_enter_delete(rdata, RPORT_EV_STOP);
mutex_unlock(&rdata->rp_mutex);
out:
@@ -357,26 +416,25 @@ out:
/**
* fc_rport_enter_ready() - The rport is ready
- * @rport: Fibre Channel remote port that is ready
+ * @rdata: private remote port
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_ready(struct fc_rport *rport)
+static void fc_rport_enter_ready(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
- fc_rport_state_enter(rport, RPORT_ST_READY);
+ fc_rport_state_enter(rdata, RPORT_ST_READY);
- FC_RPORT_DBG(rport, "Port is Ready\n");
+ FC_RPORT_DBG(rdata, "Port is Ready\n");
- rdata->event = RPORT_EV_CREATED;
- queue_work(rport_event_queue, &rdata->event_work);
+ if (rdata->event == RPORT_EV_NONE)
+ queue_work(rport_event_queue, &rdata->event_work);
+ rdata->event = RPORT_EV_READY;
}
/**
* fc_rport_timeout() - Handler for the retry_work timer.
- * @work: The work struct of the fc_rport_libfc_priv
+ * @work: The work struct of the fc_rport_priv
*
* Locking Note: Called without the rport lock held. This
* function will hold the rport lock, call an _enter_*
@@ -384,63 +442,63 @@ static void fc_rport_enter_ready(struct fc_rport *rport)
*/
static void fc_rport_timeout(struct work_struct *work)
{
- struct fc_rport_libfc_priv *rdata =
- container_of(work, struct fc_rport_libfc_priv, retry_work.work);
- struct fc_rport *rport = PRIV_TO_RPORT(rdata);
+ struct fc_rport_priv *rdata =
+ container_of(work, struct fc_rport_priv, retry_work.work);
mutex_lock(&rdata->rp_mutex);
switch (rdata->rp_state) {
case RPORT_ST_PLOGI:
- fc_rport_enter_plogi(rport);
+ fc_rport_enter_plogi(rdata);
break;
case RPORT_ST_PRLI:
- fc_rport_enter_prli(rport);
+ fc_rport_enter_prli(rdata);
break;
case RPORT_ST_RTV:
- fc_rport_enter_rtv(rport);
+ fc_rport_enter_rtv(rdata);
break;
case RPORT_ST_LOGO:
- fc_rport_enter_logo(rport);
+ fc_rport_enter_logo(rdata);
+ break;
+ case RPORT_ST_ADISC:
+ fc_rport_enter_adisc(rdata);
break;
case RPORT_ST_READY:
case RPORT_ST_INIT:
- case RPORT_ST_NONE:
+ case RPORT_ST_DELETE:
break;
}
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
}
/**
* fc_rport_error() - Error handler, called once retries have been exhausted
- * @rport: The fc_rport object
+ * @rdata: private remote port
* @fp: The frame pointer
*
* Locking Note: The rport lock is expected to be held before
* calling this routine
*/
-static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
+static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
-
- FC_RPORT_DBG(rport, "Error %ld in state %s, retries %d\n",
- PTR_ERR(fp), fc_rport_state(rport), rdata->retries);
+ FC_RPORT_DBG(rdata, "Error %ld in state %s, retries %d\n",
+ IS_ERR(fp) ? -PTR_ERR(fp) : 0,
+ fc_rport_state(rdata), rdata->retries);
switch (rdata->rp_state) {
case RPORT_ST_PLOGI:
- case RPORT_ST_PRLI:
case RPORT_ST_LOGO:
- rdata->event = RPORT_EV_FAILED;
- fc_rport_state_enter(rport, RPORT_ST_NONE);
- queue_work(rport_event_queue,
- &rdata->event_work);
+ fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
break;
case RPORT_ST_RTV:
- fc_rport_enter_ready(rport);
+ fc_rport_enter_ready(rdata);
break;
- case RPORT_ST_NONE:
+ case RPORT_ST_PRLI:
+ case RPORT_ST_ADISC:
+ fc_rport_enter_logo(rdata);
+ break;
+ case RPORT_ST_DELETE:
case RPORT_ST_READY:
case RPORT_ST_INIT:
break;
@@ -449,7 +507,7 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
/**
* fc_rport_error_retry() - Error handler when retries are desired
- * @rport: The fc_rport object
+ * @rdata: private remote port data
* @fp: The frame pointer
*
* If the error was an exchange timeout retry immediately,
@@ -458,45 +516,43 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp)
* Locking Note: The rport lock is expected to be held before
* calling this routine
*/
-static void fc_rport_error_retry(struct fc_rport *rport, struct fc_frame *fp)
+static void fc_rport_error_retry(struct fc_rport_priv *rdata,
+ struct fc_frame *fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
unsigned long delay = FC_DEF_E_D_TOV;
/* make sure this isn't an FC_EX_CLOSED error, never retry those */
if (PTR_ERR(fp) == -FC_EX_CLOSED)
- return fc_rport_error(rport, fp);
+ return fc_rport_error(rdata, fp);
if (rdata->retries < rdata->local_port->max_rport_retry_count) {
- FC_RPORT_DBG(rport, "Error %ld in state %s, retrying\n",
- PTR_ERR(fp), fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Error %ld in state %s, retrying\n",
+ PTR_ERR(fp), fc_rport_state(rdata));
rdata->retries++;
/* no additional delay on exchange timeouts */
if (PTR_ERR(fp) == -FC_EX_TIMEOUT)
delay = 0;
- get_device(&rport->dev);
schedule_delayed_work(&rdata->retry_work, delay);
return;
}
- return fc_rport_error(rport, fp);
+ return fc_rport_error(rdata, fp);
}
/**
* fc_rport_plogi_recv_resp() - Handle incoming ELS PLOGI response
* @sp: current sequence in the PLOGI exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: private remote port data
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
* and then unlock the rport.
*/
static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+ void *rdata_arg)
{
- struct fc_rport *rport = rp_arg;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_rport_priv *rdata = rdata_arg;
struct fc_lport *lport = rdata->local_port;
struct fc_els_flogi *plp = NULL;
unsigned int tov;
@@ -506,26 +562,26 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Received a PLOGI response\n");
+ FC_RPORT_DBG(rdata, "Received a PLOGI %s\n", fc_els_resp_type(fp));
if (rdata->rp_state != RPORT_ST_PLOGI) {
- FC_RPORT_DBG(rport, "Received a PLOGI response, but in state "
- "%s\n", fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received a PLOGI response, but in state "
+ "%s\n", fc_rport_state(rdata));
if (IS_ERR(fp))
goto err;
goto out;
}
if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
goto err;
}
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC &&
(plp = fc_frame_payload_get(fp, sizeof(*plp))) != NULL) {
- rport->port_name = get_unaligned_be64(&plp->fl_wwpn);
- rport->node_name = get_unaligned_be64(&plp->fl_wwnn);
+ rdata->ids.port_name = get_unaligned_be64(&plp->fl_wwpn);
+ rdata->ids.node_name = get_unaligned_be64(&plp->fl_wwnn);
tov = ntohl(plp->fl_csp.sp_e_d_tov);
if (ntohs(plp->fl_csp.sp_features) & FC_SP_FT_EDTR)
@@ -537,75 +593,64 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,
if (cssp_seq < csp_seq)
csp_seq = cssp_seq;
rdata->max_seq = csp_seq;
- rport->maxframe_size =
- fc_plogi_get_maxframe(plp, lport->mfs);
-
- /*
- * If the rport is one of the well known addresses
- * we skip PRLI and RTV and go straight to READY.
- */
- if (rport->port_id >= FC_FID_DOM_MGR)
- fc_rport_enter_ready(rport);
- else
- fc_rport_enter_prli(rport);
+ rdata->maxframe_size = fc_plogi_get_maxframe(plp, lport->mfs);
+ fc_rport_enter_prli(rdata);
} else
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
/**
* fc_rport_enter_plogi() - Send Port Login (PLOGI) request to peer
- * @rport: Fibre Channel remote port to send PLOGI to
+ * @rdata: private remote port data
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_plogi(struct fc_rport *rport)
+static void fc_rport_enter_plogi(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct fc_frame *fp;
- FC_RPORT_DBG(rport, "Port entered PLOGI state from %s state\n",
- fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Port entered PLOGI state from %s state\n",
+ fc_rport_state(rdata));
- fc_rport_state_enter(rport, RPORT_ST_PLOGI);
+ fc_rport_state_enter(rdata, RPORT_ST_PLOGI);
- rport->maxframe_size = FC_MIN_MAX_PAYLOAD;
+ rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;
fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi));
if (!fp) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
return;
}
rdata->e_d_tov = lport->e_d_tov;
- if (!lport->tt.elsct_send(lport, rport, fp, ELS_PLOGI,
- fc_rport_plogi_resp, rport, lport->e_d_tov))
- fc_rport_error_retry(rport, fp);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PLOGI,
+ fc_rport_plogi_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
else
- get_device(&rport->dev);
+ kref_get(&rdata->kref);
}
/**
* fc_rport_prli_resp() - Process Login (PRLI) response handler
* @sp: current sequence in the PRLI exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: private remote port data
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
* and then unlock the rport.
*/
static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+ void *rdata_arg)
{
- struct fc_rport *rport = rp_arg;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_rport_priv *rdata = rdata_arg;
struct {
struct fc_els_prli prli;
struct fc_els_spp spp;
@@ -616,21 +661,24 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Received a PRLI response\n");
+ FC_RPORT_DBG(rdata, "Received a PRLI %s\n", fc_els_resp_type(fp));
if (rdata->rp_state != RPORT_ST_PRLI) {
- FC_RPORT_DBG(rport, "Received a PRLI response, but in state "
- "%s\n", fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received a PRLI response, but in state "
+ "%s\n", fc_rport_state(rdata));
if (IS_ERR(fp))
goto err;
goto out;
}
if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
goto err;
}
+ /* reinitialize remote port roles */
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
op = fc_frame_payload_op(fp);
if (op == ELS_LS_ACC) {
pp = fc_frame_payload_get(fp, sizeof(*pp));
@@ -640,90 +688,82 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,
rdata->flags |= FC_RP_FLAGS_RETRY;
}
- rport->supported_classes = FC_COS_CLASS3;
+ rdata->supported_classes = FC_COS_CLASS3;
if (fcp_parm & FCP_SPPF_INIT_FCN)
roles |= FC_RPORT_ROLE_FCP_INITIATOR;
if (fcp_parm & FCP_SPPF_TARG_FCN)
roles |= FC_RPORT_ROLE_FCP_TARGET;
- rport->roles = roles;
- fc_rport_enter_rtv(rport);
+ rdata->ids.roles = roles;
+ fc_rport_enter_rtv(rdata);
} else {
- FC_RPORT_DBG(rport, "Bad ELS response for PRLI command\n");
- rdata->event = RPORT_EV_FAILED;
- fc_rport_state_enter(rport, RPORT_ST_NONE);
- queue_work(rport_event_queue, &rdata->event_work);
+ FC_RPORT_DBG(rdata, "Bad ELS response for PRLI command\n");
+ fc_rport_enter_delete(rdata, RPORT_EV_FAILED);
}
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
/**
* fc_rport_logo_resp() - Logout (LOGO) response handler
* @sp: current sequence in the LOGO exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: private remote port data
*
* Locking Note: This function will be called without the rport lock
* held, but it will lock, call an _enter_* function or fc_rport_error
* and then unlock the rport.
*/
static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+ void *rdata_arg)
{
- struct fc_rport *rport = rp_arg;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_rport_priv *rdata = rdata_arg;
u8 op;
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Received a LOGO response\n");
+ FC_RPORT_DBG(rdata, "Received a LOGO %s\n", fc_els_resp_type(fp));
if (rdata->rp_state != RPORT_ST_LOGO) {
- FC_RPORT_DBG(rport, "Received a LOGO response, but in state "
- "%s\n", fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received a LOGO response, but in state "
+ "%s\n", fc_rport_state(rdata));
if (IS_ERR(fp))
goto err;
goto out;
}
if (IS_ERR(fp)) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
goto err;
}
op = fc_frame_payload_op(fp);
- if (op == ELS_LS_ACC) {
- fc_rport_enter_rtv(rport);
- } else {
- FC_RPORT_DBG(rport, "Bad ELS response for LOGO command\n");
- rdata->event = RPORT_EV_LOGO;
- fc_rport_state_enter(rport, RPORT_ST_NONE);
- queue_work(rport_event_queue, &rdata->event_work);
- }
+ if (op != ELS_LS_ACC)
+ FC_RPORT_DBG(rdata, "Bad ELS response op %x for LOGO command\n",
+ op);
+ fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
/**
* fc_rport_enter_prli() - Send Process Login (PRLI) request to peer
- * @rport: Fibre Channel remote port to send PRLI to
+ * @rdata: private remote port data
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_prli(struct fc_rport *rport)
+static void fc_rport_enter_prli(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct {
struct fc_els_prli prli;
@@ -731,29 +771,38 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
} *pp;
struct fc_frame *fp;
- FC_RPORT_DBG(rport, "Port entered PRLI state from %s state\n",
- fc_rport_state(rport));
+ /*
+ * If the rport is one of the well known addresses
+ * we skip PRLI and RTV and go straight to READY.
+ */
+ if (rdata->ids.port_id >= FC_FID_DOM_MGR) {
+ fc_rport_enter_ready(rdata);
+ return;
+ }
+
+ FC_RPORT_DBG(rdata, "Port entered PRLI state from %s state\n",
+ fc_rport_state(rdata));
- fc_rport_state_enter(rport, RPORT_ST_PRLI);
+ fc_rport_state_enter(rdata, RPORT_ST_PRLI);
fp = fc_frame_alloc(lport, sizeof(*pp));
if (!fp) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
return;
}
- if (!lport->tt.elsct_send(lport, rport, fp, ELS_PRLI,
- fc_rport_prli_resp, rport, lport->e_d_tov))
- fc_rport_error_retry(rport, fp);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PRLI,
+ fc_rport_prli_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
else
- get_device(&rport->dev);
+ kref_get(&rdata->kref);
}
/**
* fc_rport_els_rtv_resp() - Request Timeout Value response handler
* @sp: current sequence in the RTV exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: private remote port data
*
* Many targets don't seem to support this.
*
@@ -762,26 +811,25 @@ static void fc_rport_enter_prli(struct fc_rport *rport)
* and then unlock the rport.
*/
static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
- void *rp_arg)
+ void *rdata_arg)
{
- struct fc_rport *rport = rp_arg;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
+ struct fc_rport_priv *rdata = rdata_arg;
u8 op;
mutex_lock(&rdata->rp_mutex);
- FC_RPORT_DBG(rport, "Received a RTV response\n");
+ FC_RPORT_DBG(rdata, "Received a RTV %s\n", fc_els_resp_type(fp));
if (rdata->rp_state != RPORT_ST_RTV) {
- FC_RPORT_DBG(rport, "Received a RTV response, but in state "
- "%s\n", fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received a RTV response, but in state "
+ "%s\n", fc_rport_state(rdata));
if (IS_ERR(fp))
goto err;
goto out;
}
if (IS_ERR(fp)) {
- fc_rport_error(rport, fp);
+ fc_rport_error(rdata, fp);
goto err;
}
@@ -807,184 +855,376 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp,
}
}
- fc_rport_enter_ready(rport);
+ fc_rport_enter_ready(rdata);
out:
fc_frame_free(fp);
err:
mutex_unlock(&rdata->rp_mutex);
- put_device(&rport->dev);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
}
/**
* fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request to peer
- * @rport: Fibre Channel remote port to send RTV to
+ * @rdata: private remote port data
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_rtv(struct fc_rport *rport)
+static void fc_rport_enter_rtv(struct fc_rport_priv *rdata)
{
struct fc_frame *fp;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
- FC_RPORT_DBG(rport, "Port entered RTV state from %s state\n",
- fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Port entered RTV state from %s state\n",
+ fc_rport_state(rdata));
- fc_rport_state_enter(rport, RPORT_ST_RTV);
+ fc_rport_state_enter(rdata, RPORT_ST_RTV);
fp = fc_frame_alloc(lport, sizeof(struct fc_els_rtv));
if (!fp) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
return;
}
- if (!lport->tt.elsct_send(lport, rport, fp, ELS_RTV,
- fc_rport_rtv_resp, rport, lport->e_d_tov))
- fc_rport_error_retry(rport, fp);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_RTV,
+ fc_rport_rtv_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
else
- get_device(&rport->dev);
+ kref_get(&rdata->kref);
}
/**
* fc_rport_enter_logo() - Send Logout (LOGO) request to peer
- * @rport: Fibre Channel remote port to send LOGO to
+ * @rdata: private remote port data
*
* Locking Note: The rport lock is expected to be held before calling
* this routine.
*/
-static void fc_rport_enter_logo(struct fc_rport *rport)
+static void fc_rport_enter_logo(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct fc_frame *fp;
- FC_RPORT_DBG(rport, "Port entered LOGO state from %s state\n",
- fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Port entered LOGO state from %s state\n",
+ fc_rport_state(rdata));
- fc_rport_state_enter(rport, RPORT_ST_LOGO);
+ fc_rport_state_enter(rdata, RPORT_ST_LOGO);
fp = fc_frame_alloc(lport, sizeof(struct fc_els_logo));
if (!fp) {
- fc_rport_error_retry(rport, fp);
+ fc_rport_error_retry(rdata, fp);
return;
}
- if (!lport->tt.elsct_send(lport, rport, fp, ELS_LOGO,
- fc_rport_logo_resp, rport, lport->e_d_tov))
- fc_rport_error_retry(rport, fp);
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_LOGO,
+ fc_rport_logo_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
else
- get_device(&rport->dev);
+ kref_get(&rdata->kref);
}
-
/**
- * fc_rport_recv_req() - Receive a request from a rport
- * @sp: current sequence in the PLOGI exchange
+ * fc_rport_els_adisc_resp() - Address Discovery response handler
+ * @sp: current sequence in the ADISC exchange
* @fp: response frame
- * @rp_arg: Fibre Channel remote port
+ * @rdata_arg: remote port private.
*
- * Locking Note: Called without the rport lock held. This
- * function will hold the rport lock, call an _enter_*
- * function and then unlock the rport.
+ * Locking Note: This function will be called without the rport lock
+ * held, but it will lock, call an _enter_* function or fc_rport_error
+ * and then unlock the rport.
*/
-void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
- struct fc_rport *rport)
+static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *rdata_arg)
+{
+ struct fc_rport_priv *rdata = rdata_arg;
+ struct fc_els_adisc *adisc;
+ u8 op;
+
+ mutex_lock(&rdata->rp_mutex);
+
+ FC_RPORT_DBG(rdata, "Received a ADISC response\n");
+
+ if (rdata->rp_state != RPORT_ST_ADISC) {
+ FC_RPORT_DBG(rdata, "Received a ADISC resp but in state %s\n",
+ fc_rport_state(rdata));
+ if (IS_ERR(fp))
+ goto err;
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_rport_error(rdata, fp);
+ goto err;
+ }
+
+ /*
+ * If address verification failed. Consider us logged out of the rport.
+ * Since the rport is still in discovery, we want to be
+ * logged in, so go to PLOGI state. Otherwise, go back to READY.
+ */
+ op = fc_frame_payload_op(fp);
+ adisc = fc_frame_payload_get(fp, sizeof(*adisc));
+ if (op != ELS_LS_ACC || !adisc ||
+ ntoh24(adisc->adisc_port_id) != rdata->ids.port_id ||
+ get_unaligned_be64(&adisc->adisc_wwpn) != rdata->ids.port_name ||
+ get_unaligned_be64(&adisc->adisc_wwnn) != rdata->ids.node_name) {
+ FC_RPORT_DBG(rdata, "ADISC error or mismatch\n");
+ fc_rport_enter_plogi(rdata);
+ } else {
+ FC_RPORT_DBG(rdata, "ADISC OK\n");
+ fc_rport_enter_ready(rdata);
+ }
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&rdata->rp_mutex);
+ kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);
+}
+
+/**
+ * fc_rport_enter_adisc() - Send Address Discover (ADISC) request to peer
+ * @rdata: remote port private data
+ *
+ * Locking Note: The rport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_rport_enter_adisc(struct fc_rport_priv *rdata)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+
+ FC_RPORT_DBG(rdata, "sending ADISC from %s state\n",
+ fc_rport_state(rdata));
+ fc_rport_state_enter(rdata, RPORT_ST_ADISC);
+
+ fp = fc_frame_alloc(lport, sizeof(struct fc_els_adisc));
+ if (!fp) {
+ fc_rport_error_retry(rdata, fp);
+ return;
+ }
+ if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_ADISC,
+ fc_rport_adisc_resp, rdata, lport->e_d_tov))
+ fc_rport_error_retry(rdata, fp);
+ else
+ kref_get(&rdata->kref);
+}
+
+/**
+ * fc_rport_recv_adisc_req() - Handle incoming Address Discovery (ADISC) Request
+ * @rdata: remote port private
+ * @sp: current sequence in the ADISC exchange
+ * @in_fp: ADISC request frame
+ *
+ * Locking Note: Called with the lport and rport locks held.
+ */
+static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata,
+ struct fc_seq *sp, struct fc_frame *in_fp)
+{
+ struct fc_lport *lport = rdata->local_port;
+ struct fc_frame *fp;
+ struct fc_exch *ep = fc_seq_exch(sp);
+ struct fc_els_adisc *adisc;
+ struct fc_seq_els_data rjt_data;
+ u32 f_ctl;
+
+ FC_RPORT_DBG(rdata, "Received ADISC request\n");
+
+ adisc = fc_frame_payload_get(in_fp, sizeof(*adisc));
+ if (!adisc) {
+ rjt_data.fp = NULL;
+ rjt_data.reason = ELS_RJT_PROT;
+ rjt_data.explan = ELS_EXPL_INV_LEN;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ goto drop;
+ }
+
+ fp = fc_frame_alloc(lport, sizeof(*adisc));
+ if (!fp)
+ goto drop;
+ fc_adisc_fill(lport, fp);
+ adisc = fc_frame_payload_get(fp, sizeof(*adisc));
+ adisc->adisc_cmd = ELS_LS_ACC;
+ sp = lport->tt.seq_start_next(sp);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+drop:
+ fc_frame_free(in_fp);
+}
+
+/**
+ * fc_rport_recv_els_req() - handle a validated ELS request.
+ * @lport: Fibre Channel local port
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: response frame
+ *
+ * Handle incoming ELS requests that require port login.
+ * The ELS opcode has already been validated by the caller.
+ *
+ * Locking Note: Called with the lport lock held.
+ */
+static void fc_rport_recv_els_req(struct fc_lport *lport,
+ struct fc_seq *sp, struct fc_frame *fp)
+{
+ struct fc_rport_priv *rdata;
struct fc_frame_header *fh;
struct fc_seq_els_data els_data;
- u8 op;
-
- mutex_lock(&rdata->rp_mutex);
els_data.fp = NULL;
- els_data.explan = ELS_EXPL_NONE;
- els_data.reason = ELS_RJT_NONE;
+ els_data.reason = ELS_RJT_UNAB;
+ els_data.explan = ELS_EXPL_PLOGI_REQD;
fh = fc_frame_header_get(fp);
- if (fh->fh_r_ctl == FC_RCTL_ELS_REQ && fh->fh_type == FC_TYPE_ELS) {
- op = fc_frame_payload_op(fp);
- switch (op) {
- case ELS_PLOGI:
- fc_rport_recv_plogi_req(rport, sp, fp);
- break;
- case ELS_PRLI:
- fc_rport_recv_prli_req(rport, sp, fp);
- break;
- case ELS_PRLO:
- fc_rport_recv_prlo_req(rport, sp, fp);
- break;
- case ELS_LOGO:
- fc_rport_recv_logo_req(rport, sp, fp);
- break;
- case ELS_RRQ:
- els_data.fp = fp;
- lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data);
- break;
- case ELS_REC:
- els_data.fp = fp;
- lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
- break;
- default:
- els_data.reason = ELS_RJT_UNSUP;
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
- break;
- }
+ mutex_lock(&lport->disc.disc_mutex);
+ rdata = lport->tt.rport_lookup(lport, ntoh24(fh->fh_s_id));
+ if (!rdata) {
+ mutex_unlock(&lport->disc.disc_mutex);
+ goto reject;
+ }
+ mutex_lock(&rdata->rp_mutex);
+ mutex_unlock(&lport->disc.disc_mutex);
+
+ switch (rdata->rp_state) {
+ case RPORT_ST_PRLI:
+ case RPORT_ST_RTV:
+ case RPORT_ST_READY:
+ case RPORT_ST_ADISC:
+ break;
+ default:
+ mutex_unlock(&rdata->rp_mutex);
+ goto reject;
+ }
+
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_PRLI:
+ fc_rport_recv_prli_req(rdata, sp, fp);
+ break;
+ case ELS_PRLO:
+ fc_rport_recv_prlo_req(rdata, sp, fp);
+ break;
+ case ELS_ADISC:
+ fc_rport_recv_adisc_req(rdata, sp, fp);
+ break;
+ case ELS_RRQ:
+ els_data.fp = fp;
+ lport->tt.seq_els_rsp_send(sp, ELS_RRQ, &els_data);
+ break;
+ case ELS_REC:
+ els_data.fp = fp;
+ lport->tt.seq_els_rsp_send(sp, ELS_REC, &els_data);
+ break;
+ default:
+ fc_frame_free(fp); /* can't happen */
+ break;
}
mutex_unlock(&rdata->rp_mutex);
+ return;
+
+reject:
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
+ fc_frame_free(fp);
+}
+
+/**
+ * fc_rport_recv_req() - Handle a received ELS request from a rport
+ * @sp: current sequence in the PLOGI exchange
+ * @fp: response frame
+ * @lport: Fibre Channel local port
+ *
+ * Locking Note: Called with the lport lock held.
+ */
+void fc_rport_recv_req(struct fc_seq *sp, struct fc_frame *fp,
+ struct fc_lport *lport)
+{
+ struct fc_seq_els_data els_data;
+
+ /*
+ * Handle PLOGI and LOGO requests separately, since they
+ * don't require prior login.
+ * Check for unsupported opcodes first and reject them.
+ * For some ops, it would be incorrect to reject with "PLOGI required".
+ */
+ switch (fc_frame_payload_op(fp)) {
+ case ELS_PLOGI:
+ fc_rport_recv_plogi_req(lport, sp, fp);
+ break;
+ case ELS_LOGO:
+ fc_rport_recv_logo_req(lport, sp, fp);
+ break;
+ case ELS_PRLI:
+ case ELS_PRLO:
+ case ELS_ADISC:
+ case ELS_RRQ:
+ case ELS_REC:
+ fc_rport_recv_els_req(lport, sp, fp);
+ break;
+ default:
+ fc_frame_free(fp);
+ els_data.fp = NULL;
+ els_data.reason = ELS_RJT_UNSUP;
+ els_data.explan = ELS_EXPL_NONE;
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &els_data);
+ break;
+ }
}
/**
* fc_rport_recv_plogi_req() - Handle incoming Port Login (PLOGI) request
- * @rport: Fibre Channel remote port that initiated PLOGI
+ * @lport: local port
* @sp: current sequence in the PLOGI exchange
* @fp: PLOGI request frame
*
- * Locking Note: The rport lock is exected to be held before calling
- * this function.
+ * Locking Note: The rport lock is held before calling this function.
*/
-static void fc_rport_recv_plogi_req(struct fc_rport *rport,
+static void fc_rport_recv_plogi_req(struct fc_lport *lport,
struct fc_seq *sp, struct fc_frame *rx_fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_lport *lport = rdata->local_port;
+ struct fc_disc *disc;
+ struct fc_rport_priv *rdata;
struct fc_frame *fp = rx_fp;
struct fc_exch *ep;
struct fc_frame_header *fh;
struct fc_els_flogi *pl;
struct fc_seq_els_data rjt_data;
- u32 sid;
- u64 wwpn;
- u64 wwnn;
- enum fc_els_rjt_reason reject = 0;
- u32 f_ctl;
- rjt_data.fp = NULL;
+ u32 sid, f_ctl;
+ rjt_data.fp = NULL;
fh = fc_frame_header_get(fp);
+ sid = ntoh24(fh->fh_s_id);
- FC_RPORT_DBG(rport, "Received PLOGI request while in state %s\n",
- fc_rport_state(rport));
+ FC_RPORT_ID_DBG(lport, sid, "Received PLOGI request\n");
- sid = ntoh24(fh->fh_s_id);
pl = fc_frame_payload_get(fp, sizeof(*pl));
if (!pl) {
- FC_RPORT_DBG(rport, "Received PLOGI too short\n");
- WARN_ON(1);
- /* XXX TBD: send reject? */
- fc_frame_free(fp);
- return;
+ FC_RPORT_ID_DBG(lport, sid, "Received PLOGI too short\n");
+ rjt_data.reason = ELS_RJT_PROT;
+ rjt_data.explan = ELS_EXPL_INV_LEN;
+ goto reject;
+ }
+
+ disc = &lport->disc;
+ mutex_lock(&disc->disc_mutex);
+ rdata = lport->tt.rport_create(lport, sid);
+ if (!rdata) {
+ mutex_unlock(&disc->disc_mutex);
+ rjt_data.reason = ELS_RJT_UNAB;
+ rjt_data.explan = ELS_EXPL_INSUF_RES;
+ goto reject;
}
- wwpn = get_unaligned_be64(&pl->fl_wwpn);
- wwnn = get_unaligned_be64(&pl->fl_wwnn);
+
+ mutex_lock(&rdata->rp_mutex);
+ mutex_unlock(&disc->disc_mutex);
+
+ rdata->ids.port_name = get_unaligned_be64(&pl->fl_wwpn);
+ rdata->ids.node_name = get_unaligned_be64(&pl->fl_wwnn);
/*
- * If the session was just created, possibly due to the incoming PLOGI,
+ * If the rport was just created, possibly due to the incoming PLOGI,
* set the state appropriately and accept the PLOGI.
*
* If we had also sent a PLOGI, and if the received PLOGI is from a
@@ -996,86 +1236,76 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport,
*/
switch (rdata->rp_state) {
case RPORT_ST_INIT:
- FC_RPORT_DBG(rport, "Received PLOGI, wwpn %llx state INIT "
- "- reject\n", (unsigned long long)wwpn);
- reject = ELS_RJT_UNSUP;
+ FC_RPORT_DBG(rdata, "Received PLOGI in INIT state\n");
break;
case RPORT_ST_PLOGI:
- FC_RPORT_DBG(rport, "Received PLOGI in PLOGI state %d\n",
- rdata->rp_state);
- if (wwpn < lport->wwpn)
- reject = ELS_RJT_INPROG;
+ FC_RPORT_DBG(rdata, "Received PLOGI in PLOGI state\n");
+ if (rdata->ids.port_name < lport->wwpn) {
+ mutex_unlock(&rdata->rp_mutex);
+ rjt_data.reason = ELS_RJT_INPROG;
+ rjt_data.explan = ELS_EXPL_NONE;
+ goto reject;
+ }
break;
case RPORT_ST_PRLI:
case RPORT_ST_READY:
- FC_RPORT_DBG(rport, "Received PLOGI in logged-in state %d "
+ case RPORT_ST_ADISC:
+ FC_RPORT_DBG(rdata, "Received PLOGI in logged-in state %d "
"- ignored for now\n", rdata->rp_state);
/* XXX TBD - should reset */
break;
- case RPORT_ST_NONE:
+ case RPORT_ST_DELETE:
default:
- FC_RPORT_DBG(rport, "Received PLOGI in unexpected "
- "state %d\n", rdata->rp_state);
- fc_frame_free(fp);
- return;
- break;
+ FC_RPORT_DBG(rdata, "Received PLOGI in unexpected state %d\n",
+ rdata->rp_state);
+ fc_frame_free(rx_fp);
+ goto out;
}
- if (reject) {
- rjt_data.reason = reject;
- rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
- fc_frame_free(fp);
- } else {
- fp = fc_frame_alloc(lport, sizeof(*pl));
- if (fp == NULL) {
- fp = rx_fp;
- rjt_data.reason = ELS_RJT_UNAB;
- rjt_data.explan = ELS_EXPL_NONE;
- lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
- fc_frame_free(fp);
- } else {
- sp = lport->tt.seq_start_next(sp);
- WARN_ON(!sp);
- fc_rport_set_name(rport, wwpn, wwnn);
-
- /*
- * Get session payload size from incoming PLOGI.
- */
- rport->maxframe_size =
- fc_plogi_get_maxframe(pl, lport->mfs);
- fc_frame_free(rx_fp);
- fc_plogi_fill(lport, fp, ELS_LS_ACC);
-
- /*
- * Send LS_ACC. If this fails,
- * the originator should retry.
- */
- f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ;
- f_ctl |= FC_FC_END_SEQ | FC_FC_SEQ_INIT;
- ep = fc_seq_exch(sp);
- fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
- FC_TYPE_ELS, f_ctl, 0);
- lport->tt.seq_send(lport, sp, fp);
- if (rdata->rp_state == RPORT_ST_PLOGI)
- fc_rport_enter_prli(rport);
- }
- }
+ /*
+ * Get session payload size from incoming PLOGI.
+ */
+ rdata->maxframe_size = fc_plogi_get_maxframe(pl, lport->mfs);
+ fc_frame_free(rx_fp);
+
+ /*
+ * Send LS_ACC. If this fails, the originator should retry.
+ */
+ sp = lport->tt.seq_start_next(sp);
+ if (!sp)
+ goto out;
+ fp = fc_frame_alloc(lport, sizeof(*pl));
+ if (!fp)
+ goto out;
+
+ fc_plogi_fill(lport, fp, ELS_LS_ACC);
+ f_ctl = FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;
+ ep = fc_seq_exch(sp);
+ fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid,
+ FC_TYPE_ELS, f_ctl, 0);
+ lport->tt.seq_send(lport, sp, fp);
+ fc_rport_enter_prli(rdata);
+out:
+ mutex_unlock(&rdata->rp_mutex);
+ return;
+
+reject:
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_RJT, &rjt_data);
+ fc_frame_free(fp);
}
/**
* fc_rport_recv_prli_req() - Handle incoming Process Login (PRLI) request
- * @rport: Fibre Channel remote port that initiated PRLI
+ * @rdata: private remote port data
* @sp: current sequence in the PRLI exchange
* @fp: PRLI request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
*/
-static void fc_rport_recv_prli_req(struct fc_rport *rport,
+static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,
struct fc_seq *sp, struct fc_frame *rx_fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct fc_exch *ep;
struct fc_frame *fp;
@@ -1099,12 +1329,14 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
fh = fc_frame_header_get(rx_fp);
- FC_RPORT_DBG(rport, "Received PRLI request while in state %s\n",
- fc_rport_state(rport));
+ FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",
+ fc_rport_state(rdata));
switch (rdata->rp_state) {
case RPORT_ST_PRLI:
+ case RPORT_ST_RTV:
case RPORT_ST_READY:
+ case RPORT_ST_ADISC:
reason = ELS_RJT_NONE;
break;
default:
@@ -1149,6 +1381,9 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
pp->prli.prli_len = htons(len);
len -= sizeof(struct fc_els_prli);
+ /* reinitialize remote port roles */
+ rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN;
+
/*
* Go through all the service parameter pages and build
* response. If plen indicates longer SPP than standard,
@@ -1169,12 +1404,12 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
fcp_parm = ntohl(rspp->spp_params);
if (fcp_parm * FCP_SPPF_RETRY)
rdata->flags |= FC_RP_FLAGS_RETRY;
- rport->supported_classes = FC_COS_CLASS3;
+ rdata->supported_classes = FC_COS_CLASS3;
if (fcp_parm & FCP_SPPF_INIT_FCN)
roles |= FC_RPORT_ROLE_FCP_INITIATOR;
if (fcp_parm & FCP_SPPF_TARG_FCN)
roles |= FC_RPORT_ROLE_FCP_TARGET;
- rport->roles = roles;
+ rdata->ids.roles = roles;
spp->spp_params =
htonl(lport->service_params);
@@ -1204,9 +1439,10 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
*/
switch (rdata->rp_state) {
case RPORT_ST_PRLI:
- fc_rport_enter_ready(rport);
+ fc_rport_enter_ready(rdata);
break;
case RPORT_ST_READY:
+ case RPORT_ST_ADISC:
break;
default:
break;
@@ -1217,17 +1453,17 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport,
/**
* fc_rport_recv_prlo_req() - Handle incoming Process Logout (PRLO) request
- * @rport: Fibre Channel remote port that initiated PRLO
+ * @rdata: private remote port data
* @sp: current sequence in the PRLO exchange
* @fp: PRLO request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
*/
-static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
+static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata,
+ struct fc_seq *sp,
struct fc_frame *fp)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
struct fc_lport *lport = rdata->local_port;
struct fc_frame_header *fh;
@@ -1235,13 +1471,8 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
fh = fc_frame_header_get(fp);
- FC_RPORT_DBG(rport, "Received PRLO request while in state %s\n",
- fc_rport_state(rport));
-
- if (rdata->rp_state == RPORT_ST_NONE) {
- fc_frame_free(fp);
- return;
- }
+ FC_RPORT_DBG(rdata, "Received PRLO request while in state %s\n",
+ fc_rport_state(rdata));
rjt_data.fp = NULL;
rjt_data.reason = ELS_RJT_UNAB;
@@ -1252,35 +1483,46 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp,
/**
* fc_rport_recv_logo_req() - Handle incoming Logout (LOGO) request
- * @rport: Fibre Channel remote port that initiated LOGO
+ * @lport: local port.
* @sp: current sequence in the LOGO exchange
* @fp: LOGO request frame
*
* Locking Note: The rport lock is exected to be held before calling
* this function.
*/
-static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp,
+static void fc_rport_recv_logo_req(struct fc_lport *lport,
+ struct fc_seq *sp,
struct fc_frame *fp)
{
struct fc_frame_header *fh;
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_lport *lport = rdata->local_port;
-
- fh = fc_frame_header_get(fp);
+ struct fc_rport_priv *rdata;
+ u32 sid;
- FC_RPORT_DBG(rport, "Received LOGO request while in state %s\n",
- fc_rport_state(rport));
+ lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
- if (rdata->rp_state == RPORT_ST_NONE) {
- fc_frame_free(fp);
- return;
- }
+ fh = fc_frame_header_get(fp);
+ sid = ntoh24(fh->fh_s_id);
- rdata->event = RPORT_EV_LOGO;
- fc_rport_state_enter(rport, RPORT_ST_NONE);
- queue_work(rport_event_queue, &rdata->event_work);
+ mutex_lock(&lport->disc.disc_mutex);
+ rdata = lport->tt.rport_lookup(lport, sid);
+ if (rdata) {
+ mutex_lock(&rdata->rp_mutex);
+ FC_RPORT_DBG(rdata, "Received LOGO request while in state %s\n",
+ fc_rport_state(rdata));
- lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL);
+ /*
+ * If the remote port was created due to discovery,
+ * log back in. It may have seen a stale RSCN about us.
+ */
+ if (rdata->rp_state != RPORT_ST_DELETE && rdata->disc_id)
+ fc_rport_enter_plogi(rdata);
+ else
+ fc_rport_enter_delete(rdata, RPORT_EV_LOGO);
+ mutex_unlock(&rdata->rp_mutex);
+ } else
+ FC_RPORT_ID_DBG(lport, sid,
+ "Received LOGO from non-logged-in port\n");
+ mutex_unlock(&lport->disc.disc_mutex);
fc_frame_free(fp);
}
@@ -1291,8 +1533,11 @@ static void fc_rport_flush_queue(void)
int fc_rport_init(struct fc_lport *lport)
{
+ if (!lport->tt.rport_lookup)
+ lport->tt.rport_lookup = fc_rport_lookup;
+
if (!lport->tt.rport_create)
- lport->tt.rport_create = fc_rport_rogue_create;
+ lport->tt.rport_create = fc_rport_create;
if (!lport->tt.rport_login)
lport->tt.rport_login = fc_rport_login;
@@ -1306,6 +1551,9 @@ int fc_rport_init(struct fc_lport *lport)
if (!lport->tt.rport_flush_queue)
lport->tt.rport_flush_queue = fc_rport_flush_queue;
+ if (!lport->tt.rport_destroy)
+ lport->tt.rport_destroy = fc_rport_destroy;
+
return 0;
}
EXPORT_SYMBOL(fc_rport_init);
@@ -1327,8 +1575,8 @@ EXPORT_SYMBOL(fc_destroy_rport);
void fc_rport_terminate_io(struct fc_rport *rport)
{
- struct fc_rport_libfc_priv *rdata = rport->dd_data;
- struct fc_lport *lport = rdata->local_port;
+ struct fc_rport_libfc_priv *rp = rport->dd_data;
+ struct fc_lport *lport = rp->local_port;
lport->tt.exch_mgr_reset(lport, 0, rport->port_id);
lport->tt.exch_mgr_reset(lport, rport->port_id, 0);
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index a751f6230c2..8dc73c489a1 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -109,12 +109,9 @@ inline void iscsi_conn_queue_work(struct iscsi_conn *conn)
}
EXPORT_SYMBOL_GPL(iscsi_conn_queue_work);
-void
-iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
+static void __iscsi_update_cmdsn(struct iscsi_session *session,
+ uint32_t exp_cmdsn, uint32_t max_cmdsn)
{
- uint32_t max_cmdsn = be32_to_cpu(hdr->max_cmdsn);
- uint32_t exp_cmdsn = be32_to_cpu(hdr->exp_cmdsn);
-
/*
* standard specifies this check for when to update expected and
* max sequence numbers
@@ -138,6 +135,12 @@ iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
iscsi_conn_queue_work(session->leadconn);
}
}
+
+void iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
+{
+ __iscsi_update_cmdsn(session, be32_to_cpu(hdr->exp_cmdsn),
+ be32_to_cpu(hdr->max_cmdsn));
+}
EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);
/**
@@ -301,8 +304,6 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
hdr->flags = ISCSI_ATTR_SIMPLE;
int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
memcpy(task->lun, hdr->lun, sizeof(task->lun));
- hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn);
- session->cmdsn++;
hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
cmd_len = sc->cmd_len;
if (cmd_len < ISCSI_CDB_SIZE)
@@ -388,6 +389,8 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
return -EIO;
task->state = ISCSI_TASK_RUNNING;
+ hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn);
+ session->cmdsn++;
conn->scsicmd_pdus_cnt++;
ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x "
@@ -499,6 +502,31 @@ static void iscsi_complete_task(struct iscsi_task *task, int state)
__iscsi_put_task(task);
}
+/**
+ * iscsi_complete_scsi_task - finish scsi task normally
+ * @task: iscsi task for scsi cmd
+ * @exp_cmdsn: expected cmd sn in cpu format
+ * @max_cmdsn: max cmd sn in cpu format
+ *
+ * This is used when drivers do not need or cannot perform
+ * lower level pdu processing.
+ *
+ * Called with session lock
+ */
+void iscsi_complete_scsi_task(struct iscsi_task *task,
+ uint32_t exp_cmdsn, uint32_t max_cmdsn)
+{
+ struct iscsi_conn *conn = task->conn;
+
+ ISCSI_DBG_SESSION(conn->session, "[itt 0x%x]\n", task->itt);
+
+ conn->last_recv = jiffies;
+ __iscsi_update_cmdsn(conn->session, exp_cmdsn, max_cmdsn);
+ iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
+}
+EXPORT_SYMBOL_GPL(iscsi_complete_scsi_task);
+
+
/*
* session lock must be held and if not called for a task that is
* still pending or from the xmit thread, then xmit thread must
@@ -857,27 +885,102 @@ static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
}
}
+static int iscsi_nop_out_rsp(struct iscsi_task *task,
+ struct iscsi_nopin *nop, char *data, int datalen)
+{
+ struct iscsi_conn *conn = task->conn;
+ int rc = 0;
+
+ if (conn->ping_task != task) {
+ /*
+ * If this is not in response to one of our
+ * nops then it must be from userspace.
+ */
+ if (iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *)nop,
+ data, datalen))
+ rc = ISCSI_ERR_CONN_FAILED;
+ } else
+ mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout);
+ iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
+ return rc;
+}
+
static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
char *data, int datalen)
{
struct iscsi_reject *reject = (struct iscsi_reject *)hdr;
struct iscsi_hdr rejected_pdu;
+ int opcode, rc = 0;
conn->exp_statsn = be32_to_cpu(reject->statsn) + 1;
- if (reject->reason == ISCSI_REASON_DATA_DIGEST_ERROR) {
- if (ntoh24(reject->dlength) > datalen)
- return ISCSI_ERR_PROTO;
+ if (ntoh24(reject->dlength) > datalen ||
+ ntoh24(reject->dlength) < sizeof(struct iscsi_hdr)) {
+ iscsi_conn_printk(KERN_ERR, conn, "Cannot handle rejected "
+ "pdu. Invalid data length (pdu dlength "
+ "%u, datalen %d\n", ntoh24(reject->dlength),
+ datalen);
+ return ISCSI_ERR_PROTO;
+ }
+ memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
+ opcode = rejected_pdu.opcode & ISCSI_OPCODE_MASK;
+
+ switch (reject->reason) {
+ case ISCSI_REASON_DATA_DIGEST_ERROR:
+ iscsi_conn_printk(KERN_ERR, conn,
+ "pdu (op 0x%x itt 0x%x) rejected "
+ "due to DataDigest error.\n",
+ rejected_pdu.itt, opcode);
+ break;
+ case ISCSI_REASON_IMM_CMD_REJECT:
+ iscsi_conn_printk(KERN_ERR, conn,
+ "pdu (op 0x%x itt 0x%x) rejected. Too many "
+ "immediate commands.\n",
+ rejected_pdu.itt, opcode);
+ /*
+ * We only send one TMF at a time so if the target could not
+ * handle it, then it should get fixed (RFC mandates that
+ * a target can handle one immediate TMF per conn).
+ *
+ * For nops-outs, we could have sent more than one if
+ * the target is sending us lots of nop-ins
+ */
+ if (opcode != ISCSI_OP_NOOP_OUT)
+ return 0;
- if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {
- memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
- iscsi_conn_printk(KERN_ERR, conn,
- "pdu (op 0x%x) rejected "
- "due to DataDigest error.\n",
- rejected_pdu.opcode);
+ if (rejected_pdu.itt == cpu_to_be32(ISCSI_RESERVED_TAG))
+ /*
+ * nop-out in response to target's nop-out rejected.
+ * Just resend.
+ */
+ iscsi_send_nopout(conn,
+ (struct iscsi_nopin*)&rejected_pdu);
+ else {
+ struct iscsi_task *task;
+ /*
+ * Our nop as ping got dropped. We know the target
+ * and transport are ok so just clean up
+ */
+ task = iscsi_itt_to_task(conn, rejected_pdu.itt);
+ if (!task) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "Invalid pdu reject. Could "
+ "not lookup rejected task.\n");
+ rc = ISCSI_ERR_BAD_ITT;
+ } else
+ rc = iscsi_nop_out_rsp(task,
+ (struct iscsi_nopin*)&rejected_pdu,
+ NULL, 0);
}
+ break;
+ default:
+ iscsi_conn_printk(KERN_ERR, conn,
+ "pdu (op 0x%x itt 0x%x) rejected. Reason "
+ "code 0x%x\n", rejected_pdu.itt,
+ rejected_pdu.opcode, reject->reason);
+ break;
}
- return 0;
+ return rc;
}
/**
@@ -1038,15 +1141,8 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
}
conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
- if (conn->ping_task != task)
- /*
- * If this is not in response to one of our
- * nops then it must be from userspace.
- */
- goto recv_pdu;
-
- mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout);
- iscsi_complete_task(task, ISCSI_TASK_COMPLETED);
+ rc = iscsi_nop_out_rsp(task, (struct iscsi_nopin*)hdr,
+ data, datalen);
break;
default:
rc = ISCSI_ERR_BAD_OPCODE;
@@ -1212,6 +1308,9 @@ static int iscsi_xmit_task(struct iscsi_conn *conn)
struct iscsi_task *task = conn->task;
int rc;
+ if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx))
+ return -ENODATA;
+
__iscsi_get_task(task);
spin_unlock_bh(&conn->session->lock);
rc = conn->session->tt->xmit_task(task);
@@ -1261,7 +1360,7 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
int rc = 0;
spin_lock_bh(&conn->session->lock);
- if (unlikely(conn->suspend_tx)) {
+ if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) {
ISCSI_DBG_SESSION(conn->session, "Tx suspended!\n");
spin_unlock_bh(&conn->session->lock);
return -ENODATA;
@@ -1270,7 +1369,7 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
if (conn->task) {
rc = iscsi_xmit_task(conn);
if (rc)
- goto again;
+ goto done;
}
/*
@@ -1290,7 +1389,7 @@ check_mgmt:
}
rc = iscsi_xmit_task(conn);
if (rc)
- goto again;
+ goto done;
}
/* process pending command queue */
@@ -1311,14 +1410,14 @@ check_mgmt:
list_add_tail(&conn->task->running,
&conn->cmdqueue);
conn->task = NULL;
- goto again;
+ goto done;
} else
fail_scsi_task(conn->task, DID_ABORT);
continue;
}
rc = iscsi_xmit_task(conn);
if (rc)
- goto again;
+ goto done;
/*
* we could continuously get new task requests so
* we need to check the mgmt queue for nops that need to
@@ -1344,16 +1443,14 @@ check_mgmt:
conn->task->state = ISCSI_TASK_RUNNING;
rc = iscsi_xmit_task(conn);
if (rc)
- goto again;
+ goto done;
if (!list_empty(&conn->mgmtqueue))
goto check_mgmt;
}
spin_unlock_bh(&conn->session->lock);
return -ENODATA;
-again:
- if (unlikely(conn->suspend_tx))
- rc = -ENODATA;
+done:
spin_unlock_bh(&conn->session->lock);
return rc;
}
@@ -1474,6 +1571,12 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
goto fault;
}
+ if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) {
+ reason = FAILURE_SESSION_IN_RECOVERY;
+ sc->result = DID_REQUEUE;
+ goto fault;
+ }
+
if (iscsi_check_cmdsn_window_closed(conn)) {
reason = FAILURE_WINDOW_CLOSED;
goto reject;
@@ -1497,6 +1600,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
}
}
if (session->tt->xmit_task(task)) {
+ session->cmdsn--;
reason = FAILURE_SESSION_NOT_READY;
goto prepd_reject;
}
@@ -1712,6 +1816,33 @@ static void fail_scsi_tasks(struct iscsi_conn *conn, unsigned lun,
}
}
+/**
+ * iscsi_suspend_queue - suspend iscsi_queuecommand
+ * @conn: iscsi conn to stop queueing IO on
+ *
+ * This grabs the session lock to make sure no one is in
+ * xmit_task/queuecommand, and then sets suspend to prevent
+ * new commands from being queued. This only needs to be called
+ * by offload drivers that need to sync a path like ep disconnect
+ * with the iscsi_queuecommand/xmit_task. To start IO again libiscsi
+ * will call iscsi_start_tx and iscsi_unblock_session when in FFP.
+ */
+void iscsi_suspend_queue(struct iscsi_conn *conn)
+{
+ spin_lock_bh(&conn->session->lock);
+ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+ spin_unlock_bh(&conn->session->lock);
+}
+EXPORT_SYMBOL_GPL(iscsi_suspend_queue);
+
+/**
+ * iscsi_suspend_tx - suspend iscsi_data_xmit
+ * @conn: iscsi conn tp stop processing IO on.
+ *
+ * This function sets the suspend bit to prevent iscsi_data_xmit
+ * from sending new IO, and if work is queued on the xmit thread
+ * it will wait for it to be completed.
+ */
void iscsi_suspend_tx(struct iscsi_conn *conn)
{
struct Scsi_Host *shost = conn->session->host;
diff --git a/drivers/scsi/libsrp.c b/drivers/scsi/libsrp.c
index 2742ae8a3d0..9ad38e81e34 100644
--- a/drivers/scsi/libsrp.c
+++ b/drivers/scsi/libsrp.c
@@ -124,6 +124,7 @@ static void srp_ring_free(struct device *dev, struct srp_buf **ring, size_t max,
dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma);
kfree(ring[i]);
}
+ kfree(ring);
}
int srp_target_alloc(struct srp_target *target, struct device *dev,
diff --git a/drivers/scsi/lpfc/Makefile b/drivers/scsi/lpfc/Makefile
index 1c286707dd5..ad05d6edb8f 100644
--- a/drivers/scsi/lpfc/Makefile
+++ b/drivers/scsi/lpfc/Makefile
@@ -28,4 +28,4 @@ obj-$(CONFIG_SCSI_LPFC) := lpfc.o
lpfc-objs := lpfc_mem.o lpfc_sli.o lpfc_ct.o lpfc_els.o lpfc_hbadisc.o \
lpfc_init.o lpfc_mbox.o lpfc_nportdisc.o lpfc_scsi.o lpfc_attr.o \
- lpfc_vport.o lpfc_debugfs.o
+ lpfc_vport.o lpfc_debugfs.o lpfc_bsg.o
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 1877d981183..aa10f795163 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -312,6 +312,7 @@ struct lpfc_vport {
#define FC_BYPASSED_MODE 0x20000 /* NPort is in bypassed mode */
#define FC_VPORT_NEEDS_REG_VPI 0x80000 /* Needs to have its vpi registered */
#define FC_RSCN_DEFERRED 0x100000 /* A deferred RSCN being processed */
+#define FC_VPORT_NEEDS_INIT_VPI 0x200000 /* Need to INIT_VPI before FDISC */
uint32_t ct_flags;
#define FC_CT_RFF_ID 0x1 /* RFF_ID accepted by switch */
@@ -440,6 +441,12 @@ enum intr_type_t {
MSIX,
};
+struct unsol_rcv_ct_ctx {
+ uint32_t ctxt_id;
+ uint32_t SID;
+ uint32_t oxid;
+};
+
struct lpfc_hba {
/* SCSI interface function jump table entries */
int (*lpfc_new_scsi_buf)
@@ -525,6 +532,8 @@ struct lpfc_hba {
#define FCP_XRI_ABORT_EVENT 0x20
#define ELS_XRI_ABORT_EVENT 0x40
#define ASYNC_EVENT 0x80
+#define LINK_DISABLED 0x100 /* Link disabled by user */
+#define FCF_DISC_INPROGRESS 0x200 /* FCF discovery in progress */
struct lpfc_dmabuf slim2p;
MAILBOX_t *mbox;
@@ -616,6 +625,8 @@ struct lpfc_hba {
uint32_t hbq_count; /* Count of configured HBQs */
struct hbq_s hbqs[LPFC_MAX_HBQS]; /* local copy of hbq indicies */
+ uint32_t fcp_qidx; /* next work queue to post work to */
+
unsigned long pci_bar0_map; /* Physical address for PCI BAR0 */
unsigned long pci_bar1_map; /* Physical address for PCI BAR1 */
unsigned long pci_bar2_map; /* Physical address for PCI BAR2 */
@@ -682,6 +693,7 @@ struct lpfc_hba {
struct pci_pool *lpfc_mbuf_pool;
struct pci_pool *lpfc_hrb_pool; /* header receive buffer pool */
struct pci_pool *lpfc_drb_pool; /* data receive buffer pool */
+ struct pci_pool *lpfc_hbq_pool; /* SLI3 hbq buffer pool */
struct lpfc_dma_pool lpfc_mbuf_safety_pool;
mempool_t *mbox_mem_pool;
@@ -763,11 +775,18 @@ struct lpfc_hba {
/* Maximum number of events that can be outstanding at any time*/
#define LPFC_MAX_EVT_COUNT 512
atomic_t fast_event_count;
+ uint32_t fcoe_eventtag;
+ uint32_t fcoe_eventtag_at_fcf_scan;
struct lpfc_fcf fcf;
uint8_t fc_map[3];
uint8_t valid_vlan;
uint16_t vlan_id;
struct list_head fcf_conn_rec_list;
+
+ struct mutex ct_event_mutex; /* synchronize access to ct_ev_waiters */
+ struct list_head ct_ev_waiters;
+ struct unsol_rcv_ct_ctx ct_ctx[64];
+ uint32_t ctx_idx;
};
static inline struct Scsi_Host *
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index fc07be5fbce..e1a30a16a9f 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -394,7 +394,12 @@ lpfc_link_state_show(struct device *dev, struct device_attribute *attr,
case LPFC_INIT_MBX_CMDS:
case LPFC_LINK_DOWN:
case LPFC_HBA_ERROR:
- len += snprintf(buf + len, PAGE_SIZE-len, "Link Down\n");
+ if (phba->hba_flag & LINK_DISABLED)
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ "Link Down - User disabled\n");
+ else
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ "Link Down\n");
break;
case LPFC_LINK_UP:
case LPFC_CLEAR_LA:
@@ -4127,6 +4132,9 @@ struct fc_function_template lpfc_transport_functions = {
.vport_disable = lpfc_vport_disable,
.set_vport_symbolic_name = lpfc_set_vport_symbolic_name,
+
+ .bsg_request = lpfc_bsg_request,
+ .bsg_timeout = lpfc_bsg_timeout,
};
struct fc_function_template lpfc_vport_transport_functions = {
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
new file mode 100644
index 00000000000..da6bf5aac9d
--- /dev/null
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -0,0 +1,904 @@
+/*******************************************************************
+ * This file is part of the Emulex Linux Device Driver for *
+ * Fibre Channel Host Bus Adapters. *
+ * Copyright (C) 2009 Emulex. All rights reserved. *
+ * EMULEX and SLI are trademarks of Emulex. *
+ * www.emulex.com *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of version 2 of the GNU General *
+ * Public License as published by the Free Software Foundation. *
+ * This program is distributed in the hope that it will be useful. *
+ * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
+ * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
+ * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
+ * TO BE LEGALLY INVALID. See the GNU General Public License for *
+ * more details, a copy of which can be found in the file COPYING *
+ * included with this package. *
+ *******************************************************************/
+
+#include <linux/interrupt.h>
+#include <linux/mempool.h>
+#include <linux/pci.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_transport_fc.h>
+#include <scsi/scsi_bsg_fc.h>
+
+#include "lpfc_hw4.h"
+#include "lpfc_hw.h"
+#include "lpfc_sli.h"
+#include "lpfc_sli4.h"
+#include "lpfc_nl.h"
+#include "lpfc_disc.h"
+#include "lpfc_scsi.h"
+#include "lpfc.h"
+#include "lpfc_logmsg.h"
+#include "lpfc_crtn.h"
+#include "lpfc_vport.h"
+#include "lpfc_version.h"
+
+/**
+ * lpfc_bsg_rport_ct - send a CT command from a bsg request
+ * @job: fc_bsg_job to handle
+ */
+static int
+lpfc_bsg_rport_ct(struct fc_bsg_job *job)
+{
+ struct Scsi_Host *shost = job->shost;
+ struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_rport_data *rdata = job->rport->dd_data;
+ struct lpfc_nodelist *ndlp = rdata->pnode;
+ struct ulp_bde64 *bpl = NULL;
+ uint32_t timeout;
+ struct lpfc_iocbq *cmdiocbq = NULL;
+ struct lpfc_iocbq *rspiocbq = NULL;
+ IOCB_t *cmd;
+ IOCB_t *rsp;
+ struct lpfc_dmabuf *bmp = NULL;
+ int request_nseg;
+ int reply_nseg;
+ struct scatterlist *sgel = NULL;
+ int numbde;
+ dma_addr_t busaddr;
+ int rc = 0;
+
+ /* in case no data is transferred */
+ job->reply->reply_payload_rcv_len = 0;
+
+ if (!lpfc_nlp_get(ndlp)) {
+ job->reply->result = -ENODEV;
+ return 0;
+ }
+
+ if (ndlp->nlp_flag & NLP_ELS_SND_MASK) {
+ rc = -ENODEV;
+ goto free_ndlp_exit;
+ }
+
+ spin_lock_irq(shost->host_lock);
+ cmdiocbq = lpfc_sli_get_iocbq(phba);
+ if (!cmdiocbq) {
+ rc = -ENOMEM;
+ spin_unlock_irq(shost->host_lock);
+ goto free_ndlp_exit;
+ }
+ cmd = &cmdiocbq->iocb;
+
+ rspiocbq = lpfc_sli_get_iocbq(phba);
+ if (!rspiocbq) {
+ rc = -ENOMEM;
+ goto free_cmdiocbq;
+ }
+ spin_unlock_irq(shost->host_lock);
+
+ rsp = &rspiocbq->iocb;
+
+ bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (!bmp) {
+ rc = -ENOMEM;
+ spin_lock_irq(shost->host_lock);
+ goto free_rspiocbq;
+ }
+
+ spin_lock_irq(shost->host_lock);
+ bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys);
+ if (!bmp->virt) {
+ rc = -ENOMEM;
+ goto free_bmp;
+ }
+ spin_unlock_irq(shost->host_lock);
+
+ INIT_LIST_HEAD(&bmp->list);
+ bpl = (struct ulp_bde64 *) bmp->virt;
+
+ request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) {
+ busaddr = sg_dma_address(sgel);
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ bpl->tus.f.bdeSize = sg_dma_len(sgel);
+ bpl->tus.w = cpu_to_le32(bpl->tus.w);
+ bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
+ bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
+ bpl++;
+ }
+
+ reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) {
+ busaddr = sg_dma_address(sgel);
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ bpl->tus.f.bdeSize = sg_dma_len(sgel);
+ bpl->tus.w = cpu_to_le32(bpl->tus.w);
+ bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
+ bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
+ bpl++;
+ }
+
+ cmd->un.genreq64.bdl.ulpIoTag32 = 0;
+ cmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys);
+ cmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys);
+ cmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64;
+ cmd->un.genreq64.bdl.bdeSize =
+ (request_nseg + reply_nseg) * sizeof(struct ulp_bde64);
+ cmd->ulpCommand = CMD_GEN_REQUEST64_CR;
+ cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA);
+ cmd->un.genreq64.w5.hcsw.Dfctl = 0;
+ cmd->un.genreq64.w5.hcsw.Rctl = FC_UNSOL_CTL;
+ cmd->un.genreq64.w5.hcsw.Type = FC_COMMON_TRANSPORT_ULP;
+ cmd->ulpBdeCount = 1;
+ cmd->ulpLe = 1;
+ cmd->ulpClass = CLASS3;
+ cmd->ulpContext = ndlp->nlp_rpi;
+ cmd->ulpOwner = OWN_CHIP;
+ cmdiocbq->vport = phba->pport;
+ cmdiocbq->context1 = NULL;
+ cmdiocbq->context2 = NULL;
+ cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
+
+ timeout = phba->fc_ratov * 2;
+ job->dd_data = cmdiocbq;
+
+ rc = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq, rspiocbq,
+ timeout + LPFC_DRVR_TIMEOUT);
+
+ if (rc != IOCB_TIMEDOUT) {
+ pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ }
+
+ if (rc == IOCB_TIMEDOUT) {
+ lpfc_sli_release_iocbq(phba, rspiocbq);
+ rc = -EACCES;
+ goto free_ndlp_exit;
+ }
+
+ if (rc != IOCB_SUCCESS) {
+ rc = -EACCES;
+ goto free_outdmp;
+ }
+
+ if (rsp->ulpStatus) {
+ if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
+ switch (rsp->un.ulpWord[4] & 0xff) {
+ case IOERR_SEQUENCE_TIMEOUT:
+ rc = -ETIMEDOUT;
+ break;
+ case IOERR_INVALID_RPI:
+ rc = -EFAULT;
+ break;
+ default:
+ rc = -EACCES;
+ break;
+ }
+ goto free_outdmp;
+ }
+ } else
+ job->reply->reply_payload_rcv_len =
+ rsp->un.genreq64.bdl.bdeSize;
+
+free_outdmp:
+ spin_lock_irq(shost->host_lock);
+ lpfc_mbuf_free(phba, bmp->virt, bmp->phys);
+free_bmp:
+ kfree(bmp);
+free_rspiocbq:
+ lpfc_sli_release_iocbq(phba, rspiocbq);
+free_cmdiocbq:
+ lpfc_sli_release_iocbq(phba, cmdiocbq);
+ spin_unlock_irq(shost->host_lock);
+free_ndlp_exit:
+ lpfc_nlp_put(ndlp);
+
+ /* make error code available to userspace */
+ job->reply->result = rc;
+ /* complete the job back to userspace */
+ job->job_done(job);
+
+ return 0;
+}
+
+/**
+ * lpfc_bsg_rport_els - send an ELS command from a bsg request
+ * @job: fc_bsg_job to handle
+ */
+static int
+lpfc_bsg_rport_els(struct fc_bsg_job *job)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_rport_data *rdata = job->rport->dd_data;
+ struct lpfc_nodelist *ndlp = rdata->pnode;
+
+ uint32_t elscmd;
+ uint32_t cmdsize;
+ uint32_t rspsize;
+ struct lpfc_iocbq *rspiocbq;
+ struct lpfc_iocbq *cmdiocbq;
+ IOCB_t *rsp;
+ uint16_t rpi = 0;
+ struct lpfc_dmabuf *pcmd;
+ struct lpfc_dmabuf *prsp;
+ struct lpfc_dmabuf *pbuflist = NULL;
+ struct ulp_bde64 *bpl;
+ int iocb_status;
+ int request_nseg;
+ int reply_nseg;
+ struct scatterlist *sgel = NULL;
+ int numbde;
+ dma_addr_t busaddr;
+ int rc = 0;
+
+ /* in case no data is transferred */
+ job->reply->reply_payload_rcv_len = 0;
+
+ if (!lpfc_nlp_get(ndlp)) {
+ rc = -ENODEV;
+ goto out;
+ }
+
+ elscmd = job->request->rqst_data.r_els.els_code;
+ cmdsize = job->request_payload.payload_len;
+ rspsize = job->reply_payload.payload_len;
+ rspiocbq = lpfc_sli_get_iocbq(phba);
+ if (!rspiocbq) {
+ lpfc_nlp_put(ndlp);
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ rsp = &rspiocbq->iocb;
+ rpi = ndlp->nlp_rpi;
+
+ cmdiocbq = lpfc_prep_els_iocb(phba->pport, 1, cmdsize, 0, ndlp,
+ ndlp->nlp_DID, elscmd);
+
+ if (!cmdiocbq) {
+ lpfc_sli_release_iocbq(phba, rspiocbq);
+ return -EIO;
+ }
+
+ job->dd_data = cmdiocbq;
+ pcmd = (struct lpfc_dmabuf *) cmdiocbq->context2;
+ prsp = (struct lpfc_dmabuf *) pcmd->list.next;
+
+ lpfc_mbuf_free(phba, pcmd->virt, pcmd->phys);
+ kfree(pcmd);
+ lpfc_mbuf_free(phba, prsp->virt, prsp->phys);
+ kfree(prsp);
+ cmdiocbq->context2 = NULL;
+
+ pbuflist = (struct lpfc_dmabuf *) cmdiocbq->context3;
+ bpl = (struct ulp_bde64 *) pbuflist->virt;
+
+ request_nseg = pci_map_sg(phba->pcidev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+
+ for_each_sg(job->request_payload.sg_list, sgel, request_nseg, numbde) {
+ busaddr = sg_dma_address(sgel);
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64;
+ bpl->tus.f.bdeSize = sg_dma_len(sgel);
+ bpl->tus.w = cpu_to_le32(bpl->tus.w);
+ bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
+ bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
+ bpl++;
+ }
+
+ reply_nseg = pci_map_sg(phba->pcidev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ for_each_sg(job->reply_payload.sg_list, sgel, reply_nseg, numbde) {
+ busaddr = sg_dma_address(sgel);
+ bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I;
+ bpl->tus.f.bdeSize = sg_dma_len(sgel);
+ bpl->tus.w = cpu_to_le32(bpl->tus.w);
+ bpl->addrLow = cpu_to_le32(putPaddrLow(busaddr));
+ bpl->addrHigh = cpu_to_le32(putPaddrHigh(busaddr));
+ bpl++;
+ }
+
+ cmdiocbq->iocb.un.elsreq64.bdl.bdeSize =
+ (request_nseg + reply_nseg) * sizeof(struct ulp_bde64);
+ cmdiocbq->iocb.ulpContext = rpi;
+ cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
+ cmdiocbq->context1 = NULL;
+ cmdiocbq->context2 = NULL;
+
+ iocb_status = lpfc_sli_issue_iocb_wait(phba, LPFC_ELS_RING, cmdiocbq,
+ rspiocbq, (phba->fc_ratov * 2)
+ + LPFC_DRVR_TIMEOUT);
+
+ /* release the new ndlp once the iocb completes */
+ lpfc_nlp_put(ndlp);
+ if (iocb_status != IOCB_TIMEDOUT) {
+ pci_unmap_sg(phba->pcidev, job->request_payload.sg_list,
+ job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ pci_unmap_sg(phba->pcidev, job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ }
+
+ if (iocb_status == IOCB_SUCCESS) {
+ if (rsp->ulpStatus == IOSTAT_SUCCESS) {
+ job->reply->reply_payload_rcv_len =
+ rsp->un.elsreq64.bdl.bdeSize;
+ rc = 0;
+ } else if (rsp->ulpStatus == IOSTAT_LS_RJT) {
+ struct fc_bsg_ctels_reply *els_reply;
+ /* LS_RJT data returned in word 4 */
+ uint8_t *rjt_data = (uint8_t *)&rsp->un.ulpWord[4];
+
+ els_reply = &job->reply->reply_data.ctels_reply;
+ job->reply->result = 0;
+ els_reply->status = FC_CTELS_STATUS_REJECT;
+ els_reply->rjt_data.action = rjt_data[0];
+ els_reply->rjt_data.reason_code = rjt_data[1];
+ els_reply->rjt_data.reason_explanation = rjt_data[2];
+ els_reply->rjt_data.vendor_unique = rjt_data[3];
+ } else
+ rc = -EIO;
+ } else
+ rc = -EIO;
+
+ if (iocb_status != IOCB_TIMEDOUT)
+ lpfc_els_free_iocb(phba, cmdiocbq);
+
+ lpfc_sli_release_iocbq(phba, rspiocbq);
+
+out:
+ /* make error code available to userspace */
+ job->reply->result = rc;
+ /* complete the job back to userspace */
+ job->job_done(job);
+
+ return 0;
+}
+
+struct lpfc_ct_event {
+ struct list_head node;
+ int ref;
+ wait_queue_head_t wq;
+
+ /* Event type and waiter identifiers */
+ uint32_t type_mask;
+ uint32_t req_id;
+ uint32_t reg_id;
+
+ /* next two flags are here for the auto-delete logic */
+ unsigned long wait_time_stamp;
+ int waiting;
+
+ /* seen and not seen events */
+ struct list_head events_to_get;
+ struct list_head events_to_see;
+};
+
+struct event_data {
+ struct list_head node;
+ uint32_t type;
+ uint32_t immed_dat;
+ void *data;
+ uint32_t len;
+};
+
+static struct lpfc_ct_event *
+lpfc_ct_event_new(int ev_reg_id, uint32_t ev_req_id)
+{
+ struct lpfc_ct_event *evt = kzalloc(sizeof(*evt), GFP_KERNEL);
+ if (!evt)
+ return NULL;
+
+ INIT_LIST_HEAD(&evt->events_to_get);
+ INIT_LIST_HEAD(&evt->events_to_see);
+ evt->req_id = ev_req_id;
+ evt->reg_id = ev_reg_id;
+ evt->wait_time_stamp = jiffies;
+ init_waitqueue_head(&evt->wq);
+
+ return evt;
+}
+
+static void
+lpfc_ct_event_free(struct lpfc_ct_event *evt)
+{
+ struct event_data *ed;
+
+ list_del(&evt->node);
+
+ while (!list_empty(&evt->events_to_get)) {
+ ed = list_entry(evt->events_to_get.next, typeof(*ed), node);
+ list_del(&ed->node);
+ kfree(ed->data);
+ kfree(ed);
+ }
+
+ while (!list_empty(&evt->events_to_see)) {
+ ed = list_entry(evt->events_to_see.next, typeof(*ed), node);
+ list_del(&ed->node);
+ kfree(ed->data);
+ kfree(ed);
+ }
+
+ kfree(evt);
+}
+
+static inline void
+lpfc_ct_event_ref(struct lpfc_ct_event *evt)
+{
+ evt->ref++;
+}
+
+static inline void
+lpfc_ct_event_unref(struct lpfc_ct_event *evt)
+{
+ if (--evt->ref < 0)
+ lpfc_ct_event_free(evt);
+}
+
+#define SLI_CT_ELX_LOOPBACK 0x10
+
+enum ELX_LOOPBACK_CMD {
+ ELX_LOOPBACK_XRI_SETUP,
+ ELX_LOOPBACK_DATA,
+};
+
+/**
+ * lpfc_bsg_ct_unsol_event - process an unsolicited CT command
+ * @phba:
+ * @pring:
+ * @piocbq:
+ *
+ * This function is called when an unsolicited CT command is received. It
+ * forwards the event to any processes registerd to receive CT events.
+ */
+void
+lpfc_bsg_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
+ struct lpfc_iocbq *piocbq)
+{
+ uint32_t evt_req_id = 0;
+ uint32_t cmd;
+ uint32_t len;
+ struct lpfc_dmabuf *dmabuf = NULL;
+ struct lpfc_ct_event *evt;
+ struct event_data *evt_dat = NULL;
+ struct lpfc_iocbq *iocbq;
+ size_t offset = 0;
+ struct list_head head;
+ struct ulp_bde64 *bde;
+ dma_addr_t dma_addr;
+ int i;
+ struct lpfc_dmabuf *bdeBuf1 = piocbq->context2;
+ struct lpfc_dmabuf *bdeBuf2 = piocbq->context3;
+ struct lpfc_hbq_entry *hbqe;
+ struct lpfc_sli_ct_request *ct_req;
+
+ INIT_LIST_HEAD(&head);
+ list_add_tail(&head, &piocbq->list);
+
+ if (piocbq->iocb.ulpBdeCount == 0 ||
+ piocbq->iocb.un.cont64[0].tus.f.bdeSize == 0)
+ goto error_ct_unsol_exit;
+
+ if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED)
+ dmabuf = bdeBuf1;
+ else {
+ dma_addr = getPaddr(piocbq->iocb.un.cont64[0].addrHigh,
+ piocbq->iocb.un.cont64[0].addrLow);
+ dmabuf = lpfc_sli_ringpostbuf_get(phba, pring, dma_addr);
+ }
+
+ ct_req = (struct lpfc_sli_ct_request *)dmabuf->virt;
+ evt_req_id = ct_req->FsType;
+ cmd = ct_req->CommandResponse.bits.CmdRsp;
+ len = ct_req->CommandResponse.bits.Size;
+ if (!(phba->sli3_options & LPFC_SLI3_HBQ_ENABLED))
+ lpfc_sli_ringpostbuf_put(phba, pring, dmabuf);
+
+ mutex_lock(&phba->ct_event_mutex);
+ list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
+ if (evt->req_id != evt_req_id)
+ continue;
+
+ lpfc_ct_event_ref(evt);
+
+ evt_dat = kzalloc(sizeof(*evt_dat), GFP_KERNEL);
+ if (!evt_dat) {
+ lpfc_ct_event_unref(evt);
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2614 Memory allocation failed for "
+ "CT event\n");
+ break;
+ }
+
+ mutex_unlock(&phba->ct_event_mutex);
+
+ if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
+ /* take accumulated byte count from the last iocbq */
+ iocbq = list_entry(head.prev, typeof(*iocbq), list);
+ evt_dat->len = iocbq->iocb.unsli3.rcvsli3.acc_len;
+ } else {
+ list_for_each_entry(iocbq, &head, list) {
+ for (i = 0; i < iocbq->iocb.ulpBdeCount; i++)
+ evt_dat->len +=
+ iocbq->iocb.un.cont64[i].tus.f.bdeSize;
+ }
+ }
+
+ evt_dat->data = kzalloc(evt_dat->len, GFP_KERNEL);
+ if (!evt_dat->data) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2615 Memory allocation failed for "
+ "CT event data, size %d\n",
+ evt_dat->len);
+ kfree(evt_dat);
+ mutex_lock(&phba->ct_event_mutex);
+ lpfc_ct_event_unref(evt);
+ mutex_unlock(&phba->ct_event_mutex);
+ goto error_ct_unsol_exit;
+ }
+
+ list_for_each_entry(iocbq, &head, list) {
+ if (phba->sli3_options & LPFC_SLI3_HBQ_ENABLED) {
+ bdeBuf1 = iocbq->context2;
+ bdeBuf2 = iocbq->context3;
+ }
+ for (i = 0; i < iocbq->iocb.ulpBdeCount; i++) {
+ int size = 0;
+ if (phba->sli3_options &
+ LPFC_SLI3_HBQ_ENABLED) {
+ if (i == 0) {
+ hbqe = (struct lpfc_hbq_entry *)
+ &iocbq->iocb.un.ulpWord[0];
+ size = hbqe->bde.tus.f.bdeSize;
+ dmabuf = bdeBuf1;
+ } else if (i == 1) {
+ hbqe = (struct lpfc_hbq_entry *)
+ &iocbq->iocb.unsli3.
+ sli3Words[4];
+ size = hbqe->bde.tus.f.bdeSize;
+ dmabuf = bdeBuf2;
+ }
+ if ((offset + size) > evt_dat->len)
+ size = evt_dat->len - offset;
+ } else {
+ size = iocbq->iocb.un.cont64[i].
+ tus.f.bdeSize;
+ bde = &iocbq->iocb.un.cont64[i];
+ dma_addr = getPaddr(bde->addrHigh,
+ bde->addrLow);
+ dmabuf = lpfc_sli_ringpostbuf_get(phba,
+ pring, dma_addr);
+ }
+ if (!dmabuf) {
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_LIBDFC, "2616 No dmabuf "
+ "found for iocbq 0x%p\n",
+ iocbq);
+ kfree(evt_dat->data);
+ kfree(evt_dat);
+ mutex_lock(&phba->ct_event_mutex);
+ lpfc_ct_event_unref(evt);
+ mutex_unlock(&phba->ct_event_mutex);
+ goto error_ct_unsol_exit;
+ }
+ memcpy((char *)(evt_dat->data) + offset,
+ dmabuf->virt, size);
+ offset += size;
+ if (evt_req_id != SLI_CT_ELX_LOOPBACK &&
+ !(phba->sli3_options &
+ LPFC_SLI3_HBQ_ENABLED)) {
+ lpfc_sli_ringpostbuf_put(phba, pring,
+ dmabuf);
+ } else {
+ switch (cmd) {
+ case ELX_LOOPBACK_XRI_SETUP:
+ if (!(phba->sli3_options &
+ LPFC_SLI3_HBQ_ENABLED))
+ lpfc_post_buffer(phba,
+ pring,
+ 1);
+ else
+ lpfc_in_buf_free(phba,
+ dmabuf);
+ break;
+ default:
+ if (!(phba->sli3_options &
+ LPFC_SLI3_HBQ_ENABLED))
+ lpfc_post_buffer(phba,
+ pring,
+ 1);
+ break;
+ }
+ }
+ }
+ }
+
+ mutex_lock(&phba->ct_event_mutex);
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ evt_dat->immed_dat = phba->ctx_idx;
+ phba->ctx_idx = (phba->ctx_idx + 1) % 64;
+ phba->ct_ctx[evt_dat->immed_dat].oxid =
+ piocbq->iocb.ulpContext;
+ phba->ct_ctx[evt_dat->immed_dat].SID =
+ piocbq->iocb.un.rcvels.remoteID;
+ } else
+ evt_dat->immed_dat = piocbq->iocb.ulpContext;
+
+ evt_dat->type = FC_REG_CT_EVENT;
+ list_add(&evt_dat->node, &evt->events_to_see);
+ wake_up_interruptible(&evt->wq);
+ lpfc_ct_event_unref(evt);
+ if (evt_req_id == SLI_CT_ELX_LOOPBACK)
+ break;
+ }
+ mutex_unlock(&phba->ct_event_mutex);
+
+error_ct_unsol_exit:
+ if (!list_empty(&head))
+ list_del(&head);
+
+ return;
+}
+
+/**
+ * lpfc_bsg_set_event - process a SET_EVENT bsg vendor command
+ * @job: SET_EVENT fc_bsg_job
+ */
+static int
+lpfc_bsg_set_event(struct fc_bsg_job *job)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct set_ct_event *event_req;
+ struct lpfc_ct_event *evt;
+ int rc = 0;
+
+ if (job->request_len <
+ sizeof(struct fc_bsg_request) + sizeof(struct set_ct_event)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2612 Received SET_CT_EVENT below minimum "
+ "size\n");
+ return -EINVAL;
+ }
+
+ event_req = (struct set_ct_event *)
+ job->request->rqst_data.h_vendor.vendor_cmd;
+
+ mutex_lock(&phba->ct_event_mutex);
+ list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
+ if (evt->reg_id == event_req->ev_reg_id) {
+ lpfc_ct_event_ref(evt);
+ evt->wait_time_stamp = jiffies;
+ break;
+ }
+ }
+ mutex_unlock(&phba->ct_event_mutex);
+
+ if (&evt->node == &phba->ct_ev_waiters) {
+ /* no event waiting struct yet - first call */
+ evt = lpfc_ct_event_new(event_req->ev_reg_id,
+ event_req->ev_req_id);
+ if (!evt) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2617 Failed allocation of event "
+ "waiter\n");
+ return -ENOMEM;
+ }
+
+ mutex_lock(&phba->ct_event_mutex);
+ list_add(&evt->node, &phba->ct_ev_waiters);
+ lpfc_ct_event_ref(evt);
+ mutex_unlock(&phba->ct_event_mutex);
+ }
+
+ evt->waiting = 1;
+ if (wait_event_interruptible(evt->wq,
+ !list_empty(&evt->events_to_see))) {
+ mutex_lock(&phba->ct_event_mutex);
+ lpfc_ct_event_unref(evt); /* release ref */
+ lpfc_ct_event_unref(evt); /* delete */
+ mutex_unlock(&phba->ct_event_mutex);
+ rc = -EINTR;
+ goto set_event_out;
+ }
+
+ evt->wait_time_stamp = jiffies;
+ evt->waiting = 0;
+
+ mutex_lock(&phba->ct_event_mutex);
+ list_move(evt->events_to_see.prev, &evt->events_to_get);
+ lpfc_ct_event_unref(evt); /* release ref */
+ mutex_unlock(&phba->ct_event_mutex);
+
+set_event_out:
+ /* set_event carries no reply payload */
+ job->reply->reply_payload_rcv_len = 0;
+ /* make error code available to userspace */
+ job->reply->result = rc;
+ /* complete the job back to userspace */
+ job->job_done(job);
+
+ return 0;
+}
+
+/**
+ * lpfc_bsg_get_event - process a GET_EVENT bsg vendor command
+ * @job: GET_EVENT fc_bsg_job
+ */
+static int
+lpfc_bsg_get_event(struct fc_bsg_job *job)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct get_ct_event *event_req;
+ struct get_ct_event_reply *event_reply;
+ struct lpfc_ct_event *evt;
+ struct event_data *evt_dat = NULL;
+ int rc = 0;
+
+ if (job->request_len <
+ sizeof(struct fc_bsg_request) + sizeof(struct get_ct_event)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2613 Received GET_CT_EVENT request below "
+ "minimum size\n");
+ return -EINVAL;
+ }
+
+ event_req = (struct get_ct_event *)
+ job->request->rqst_data.h_vendor.vendor_cmd;
+
+ event_reply = (struct get_ct_event_reply *)
+ job->reply->reply_data.vendor_reply.vendor_rsp;
+
+ mutex_lock(&phba->ct_event_mutex);
+ list_for_each_entry(evt, &phba->ct_ev_waiters, node) {
+ if (evt->reg_id == event_req->ev_reg_id) {
+ if (list_empty(&evt->events_to_get))
+ break;
+ lpfc_ct_event_ref(evt);
+ evt->wait_time_stamp = jiffies;
+ evt_dat = list_entry(evt->events_to_get.prev,
+ struct event_data, node);
+ list_del(&evt_dat->node);
+ break;
+ }
+ }
+ mutex_unlock(&phba->ct_event_mutex);
+
+ if (!evt_dat) {
+ job->reply->reply_payload_rcv_len = 0;
+ rc = -ENOENT;
+ goto error_get_event_exit;
+ }
+
+ if (evt_dat->len > job->reply_payload.payload_len) {
+ evt_dat->len = job->reply_payload.payload_len;
+ lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC,
+ "2618 Truncated event data at %d "
+ "bytes\n",
+ job->reply_payload.payload_len);
+ }
+
+ event_reply->immed_data = evt_dat->immed_dat;
+
+ if (evt_dat->len > 0)
+ job->reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(job->reply_payload.sg_list,
+ job->reply_payload.sg_cnt,
+ evt_dat->data, evt_dat->len);
+ else
+ job->reply->reply_payload_rcv_len = 0;
+ rc = 0;
+
+ if (evt_dat)
+ kfree(evt_dat->data);
+ kfree(evt_dat);
+ mutex_lock(&phba->ct_event_mutex);
+ lpfc_ct_event_unref(evt);
+ mutex_unlock(&phba->ct_event_mutex);
+
+error_get_event_exit:
+ /* make error code available to userspace */
+ job->reply->result = rc;
+ /* complete the job back to userspace */
+ job->job_done(job);
+
+ return rc;
+}
+
+/**
+ * lpfc_bsg_hst_vendor - process a vendor-specific fc_bsg_job
+ * @job: fc_bsg_job to handle
+ */
+static int
+lpfc_bsg_hst_vendor(struct fc_bsg_job *job)
+{
+ int command = job->request->rqst_data.h_vendor.vendor_cmd[0];
+
+ switch (command) {
+ case LPFC_BSG_VENDOR_SET_CT_EVENT:
+ return lpfc_bsg_set_event(job);
+ break;
+
+ case LPFC_BSG_VENDOR_GET_CT_EVENT:
+ return lpfc_bsg_get_event(job);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * lpfc_bsg_request - handle a bsg request from the FC transport
+ * @job: fc_bsg_job to handle
+ */
+int
+lpfc_bsg_request(struct fc_bsg_job *job)
+{
+ uint32_t msgcode;
+ int rc = -EINVAL;
+
+ msgcode = job->request->msgcode;
+
+ switch (msgcode) {
+ case FC_BSG_HST_VENDOR:
+ rc = lpfc_bsg_hst_vendor(job);
+ break;
+ case FC_BSG_RPT_ELS:
+ rc = lpfc_bsg_rport_els(job);
+ break;
+ case FC_BSG_RPT_CT:
+ rc = lpfc_bsg_rport_ct(job);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * lpfc_bsg_timeout - handle timeout of a bsg request from the FC transport
+ * @job: fc_bsg_job that has timed out
+ *
+ * This function just aborts the job's IOCB. The aborted IOCB will return to
+ * the waiting function which will handle passing the error back to userspace
+ */
+int
+lpfc_bsg_timeout(struct fc_bsg_job *job)
+{
+ struct lpfc_vport *vport = (struct lpfc_vport *)job->shost->hostdata;
+ struct lpfc_hba *phba = vport->phba;
+ struct lpfc_iocbq *cmdiocb = (struct lpfc_iocbq *)job->dd_data;
+ struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+
+ if (cmdiocb)
+ lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb);
+
+ return 0;
+}
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index d2a922997c0..0830f37409a 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -21,9 +21,11 @@
typedef int (*node_filter)(struct lpfc_nodelist *, void *);
struct fc_rport;
-void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
+void lpfc_down_link(struct lpfc_hba *, LPFC_MBOXQ_t *);
+void lpfc_sli_read_link_ste(struct lpfc_hba *);
+void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t, uint16_t);
void lpfc_dump_wakeup_param(struct lpfc_hba *, LPFC_MBOXQ_t *);
-void lpfc_dump_static_vport(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
+int lpfc_dump_static_vport(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
int lpfc_dump_fcoe_param(struct lpfc_hba *, struct lpfcMboxq *);
void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *);
void lpfc_config_async(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
@@ -135,6 +137,9 @@ int lpfc_els_disc_adisc(struct lpfc_vport *);
int lpfc_els_disc_plogi(struct lpfc_vport *);
void lpfc_els_timeout(unsigned long);
void lpfc_els_timeout_handler(struct lpfc_vport *);
+struct lpfc_iocbq *lpfc_prep_els_iocb(struct lpfc_vport *, uint8_t, uint16_t,
+ uint8_t, struct lpfc_nodelist *,
+ uint32_t, uint32_t);
void lpfc_hb_timeout_handler(struct lpfc_hba *);
void lpfc_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
@@ -182,11 +187,12 @@ int lpfc_mbox_dev_check(struct lpfc_hba *);
int lpfc_mbox_tmo_val(struct lpfc_hba *, int);
void lpfc_init_vfi(struct lpfcMboxq *, struct lpfc_vport *);
void lpfc_reg_vfi(struct lpfcMboxq *, struct lpfc_vport *, dma_addr_t);
-void lpfc_init_vpi(struct lpfcMboxq *, uint16_t);
+void lpfc_init_vpi(struct lpfc_hba *, struct lpfcMboxq *, uint16_t);
void lpfc_unreg_vfi(struct lpfcMboxq *, uint16_t);
void lpfc_reg_fcfi(struct lpfc_hba *, struct lpfcMboxq *);
void lpfc_unreg_fcfi(struct lpfcMboxq *, uint16_t);
void lpfc_resume_rpi(struct lpfcMboxq *, struct lpfc_nodelist *);
+int lpfc_check_pending_fcoe_event(struct lpfc_hba *, uint8_t);
void lpfc_config_hbq(struct lpfc_hba *, uint32_t, struct lpfc_hbq_init *,
uint32_t , LPFC_MBOXQ_t *);
@@ -234,6 +240,7 @@ void lpfc_sli_def_mbox_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
int lpfc_sli_issue_iocb(struct lpfc_hba *, uint32_t,
struct lpfc_iocbq *, uint32_t);
void lpfc_sli_pcimem_bcopy(void *, void *, uint32_t);
+void lpfc_sli_bemem_bcopy(void *, void *, uint32_t);
void lpfc_sli_abort_iocb_ring(struct lpfc_hba *, struct lpfc_sli_ring *);
void lpfc_sli_flush_fcp_rings(struct lpfc_hba *);
int lpfc_sli_ringpostbuf_put(struct lpfc_hba *, struct lpfc_sli_ring *,
@@ -360,3 +367,8 @@ void lpfc_start_fdiscs(struct lpfc_hba *phba);
#define HBA_EVENT_LINK_UP 2
#define HBA_EVENT_LINK_DOWN 3
+/* functions to support SGIOv4/bsg interface */
+int lpfc_bsg_request(struct fc_bsg_job *);
+int lpfc_bsg_timeout(struct fc_bsg_job *);
+void lpfc_bsg_ct_unsol_event(struct lpfc_hba *, struct lpfc_sli_ring *,
+ struct lpfc_iocbq *);
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 0e532f072eb..9df7ed38e1b 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -97,6 +97,8 @@ lpfc_ct_unsol_event(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
struct list_head head;
struct lpfc_dmabuf *bdeBuf;
+ lpfc_bsg_ct_unsol_event(phba, pring, piocbq);
+
if (unlikely(icmd->ulpStatus == IOSTAT_NEED_BUFFER)) {
lpfc_sli_hbqbuf_add_hbqs(phba, LPFC_ELS_HBQ);
} else if ((icmd->ulpStatus == IOSTAT_LOCAL_REJECT) &&
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index f72fdf23bf1..45337cd23fe 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -146,7 +146,7 @@ lpfc_els_chk_latt(struct lpfc_vport *vport)
* Pointer to the newly allocated/prepared els iocb data structure
* NULL - when els iocb data structure allocation/preparation failed
**/
-static struct lpfc_iocbq *
+struct lpfc_iocbq *
lpfc_prep_els_iocb(struct lpfc_vport *vport, uint8_t expectRsp,
uint16_t cmdSize, uint8_t retry,
struct lpfc_nodelist *ndlp, uint32_t did,
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index ed46b24a338..e6a47e25b21 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -61,6 +61,7 @@ static uint8_t lpfcAlpaArray[] = {
static void lpfc_disc_timeout_handler(struct lpfc_vport *);
static void lpfc_disc_flush_list(struct lpfc_vport *vport);
+static void lpfc_unregister_fcfi_cmpl(struct lpfc_hba *, LPFC_MBOXQ_t *);
void
lpfc_terminate_rport_io(struct fc_rport *rport)
@@ -1009,9 +1010,15 @@ lpfc_mbx_cmpl_reg_fcfi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
spin_lock_irqsave(&phba->hbalock, flags);
phba->fcf.fcf_flag |= FCF_REGISTERED;
spin_unlock_irqrestore(&phba->hbalock, flags);
+ /* If there is a pending FCoE event, restart FCF table scan. */
+ if (lpfc_check_pending_fcoe_event(phba, 1)) {
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ return;
+ }
if (vport->port_state != LPFC_FLOGI) {
spin_lock_irqsave(&phba->hbalock, flags);
phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
spin_unlock_irqrestore(&phba->hbalock, flags);
lpfc_initial_flogi(vport);
}
@@ -1054,6 +1061,39 @@ lpfc_fab_name_match(uint8_t *fab_name, struct fcf_record *new_fcf_record)
}
/**
+ * lpfc_sw_name_match - Check if the fcf switch name match.
+ * @fab_name: pointer to fabric name.
+ * @new_fcf_record: pointer to fcf record.
+ *
+ * This routine compare the fcf record's switch name with provided
+ * switch name. If the switch name are identical this function
+ * returns 1 else return 0.
+ **/
+static uint32_t
+lpfc_sw_name_match(uint8_t *sw_name, struct fcf_record *new_fcf_record)
+{
+ if ((sw_name[0] ==
+ bf_get(lpfc_fcf_record_switch_name_0, new_fcf_record)) &&
+ (sw_name[1] ==
+ bf_get(lpfc_fcf_record_switch_name_1, new_fcf_record)) &&
+ (sw_name[2] ==
+ bf_get(lpfc_fcf_record_switch_name_2, new_fcf_record)) &&
+ (sw_name[3] ==
+ bf_get(lpfc_fcf_record_switch_name_3, new_fcf_record)) &&
+ (sw_name[4] ==
+ bf_get(lpfc_fcf_record_switch_name_4, new_fcf_record)) &&
+ (sw_name[5] ==
+ bf_get(lpfc_fcf_record_switch_name_5, new_fcf_record)) &&
+ (sw_name[6] ==
+ bf_get(lpfc_fcf_record_switch_name_6, new_fcf_record)) &&
+ (sw_name[7] ==
+ bf_get(lpfc_fcf_record_switch_name_7, new_fcf_record)))
+ return 1;
+ else
+ return 0;
+}
+
+/**
* lpfc_mac_addr_match - Check if the fcf mac address match.
* @phba: pointer to lpfc hba data structure.
* @new_fcf_record: pointer to fcf record.
@@ -1123,6 +1163,22 @@ lpfc_copy_fcf_record(struct lpfc_hba *phba, struct fcf_record *new_fcf_record)
bf_get(lpfc_fcf_record_mac_5, new_fcf_record);
phba->fcf.fcf_indx = bf_get(lpfc_fcf_record_fcf_index, new_fcf_record);
phba->fcf.priority = new_fcf_record->fip_priority;
+ phba->fcf.switch_name[0] =
+ bf_get(lpfc_fcf_record_switch_name_0, new_fcf_record);
+ phba->fcf.switch_name[1] =
+ bf_get(lpfc_fcf_record_switch_name_1, new_fcf_record);
+ phba->fcf.switch_name[2] =
+ bf_get(lpfc_fcf_record_switch_name_2, new_fcf_record);
+ phba->fcf.switch_name[3] =
+ bf_get(lpfc_fcf_record_switch_name_3, new_fcf_record);
+ phba->fcf.switch_name[4] =
+ bf_get(lpfc_fcf_record_switch_name_4, new_fcf_record);
+ phba->fcf.switch_name[5] =
+ bf_get(lpfc_fcf_record_switch_name_5, new_fcf_record);
+ phba->fcf.switch_name[6] =
+ bf_get(lpfc_fcf_record_switch_name_6, new_fcf_record);
+ phba->fcf.switch_name[7] =
+ bf_get(lpfc_fcf_record_switch_name_7, new_fcf_record);
}
/**
@@ -1150,6 +1206,7 @@ lpfc_register_fcf(struct lpfc_hba *phba)
/* The FCF is already registered, start discovery */
if (phba->fcf.fcf_flag & FCF_REGISTERED) {
phba->fcf.fcf_flag |= (FCF_DISCOVERED | FCF_IN_USE);
+ phba->hba_flag &= ~FCF_DISC_INPROGRESS;
spin_unlock_irqrestore(&phba->hbalock, flags);
if (phba->pport->port_state != LPFC_FLOGI)
lpfc_initial_flogi(phba->pport);
@@ -1239,9 +1296,12 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
if ((conn_entry->conn_rec.flags & FCFCNCT_FBNM_VALID) &&
!lpfc_fab_name_match(conn_entry->conn_rec.fabric_name,
- new_fcf_record))
+ new_fcf_record))
+ continue;
+ if ((conn_entry->conn_rec.flags & FCFCNCT_SWNM_VALID) &&
+ !lpfc_sw_name_match(conn_entry->conn_rec.switch_name,
+ new_fcf_record))
continue;
-
if (conn_entry->conn_rec.flags & FCFCNCT_VLAN_VALID) {
/*
* If the vlan bit map does not have the bit set for the
@@ -1336,6 +1396,60 @@ lpfc_match_fcf_conn_list(struct lpfc_hba *phba,
}
/**
+ * lpfc_check_pending_fcoe_event - Check if there is pending fcoe event.
+ * @phba: pointer to lpfc hba data structure.
+ * @unreg_fcf: Unregister FCF if FCF table need to be re-scaned.
+ *
+ * This function check if there is any fcoe event pending while driver
+ * scan FCF entries. If there is any pending event, it will restart the
+ * FCF saning and return 1 else return 0.
+ */
+int
+lpfc_check_pending_fcoe_event(struct lpfc_hba *phba, uint8_t unreg_fcf)
+{
+ LPFC_MBOXQ_t *mbox;
+ int rc;
+ /*
+ * If the Link is up and no FCoE events while in the
+ * FCF discovery, no need to restart FCF discovery.
+ */
+ if ((phba->link_state >= LPFC_LINK_UP) &&
+ (phba->fcoe_eventtag == phba->fcoe_eventtag_at_fcf_scan))
+ return 0;
+
+ spin_lock_irq(&phba->hbalock);
+ phba->fcf.fcf_flag &= ~FCF_AVAILABLE;
+ spin_unlock_irq(&phba->hbalock);
+
+ if (phba->link_state >= LPFC_LINK_UP)
+ lpfc_sli4_read_fcf_record(phba, LPFC_FCOE_FCF_GET_FIRST);
+
+ if (unreg_fcf) {
+ spin_lock_irq(&phba->hbalock);
+ phba->fcf.fcf_flag &= ~FCF_REGISTERED;
+ spin_unlock_irq(&phba->hbalock);
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_ERR,
+ LOG_DISCOVERY|LOG_MBOX,
+ "2610 UNREG_FCFI mbox allocation failed\n");
+ return 1;
+ }
+ lpfc_unreg_fcfi(mbox, phba->fcf.fcfi);
+ mbox->vport = phba->pport;
+ mbox->mbox_cmpl = lpfc_unregister_fcfi_cmpl;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY|LOG_MBOX,
+ "2611 UNREG_FCFI issue mbox failed\n");
+ mempool_free(mbox, phba->mbox_mem_pool);
+ }
+ }
+
+ return 1;
+}
+
+/**
* lpfc_mbx_cmpl_read_fcf_record - Completion handler for read_fcf mbox.
* @phba: pointer to lpfc hba data structure.
* @mboxq: pointer to mailbox object.
@@ -1367,6 +1481,12 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
unsigned long flags;
uint16_t vlan_id;
+ /* If there is pending FCoE event restart FCF table scan */
+ if (lpfc_check_pending_fcoe_event(phba, 0)) {
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ return;
+ }
+
/* Get the first SGE entry from the non-embedded DMA memory. This
* routine only uses a single SGE.
*/
@@ -1424,7 +1544,9 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
spin_lock_irqsave(&phba->hbalock, flags);
if (phba->fcf.fcf_flag & FCF_IN_USE) {
if (lpfc_fab_name_match(phba->fcf.fabric_name,
- new_fcf_record) &&
+ new_fcf_record) &&
+ lpfc_sw_name_match(phba->fcf.switch_name,
+ new_fcf_record) &&
lpfc_mac_addr_match(phba, new_fcf_record)) {
phba->fcf.fcf_flag |= FCF_AVAILABLE;
spin_unlock_irqrestore(&phba->hbalock, flags);
@@ -1464,9 +1586,9 @@ lpfc_mbx_cmpl_read_fcf_record(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
* If there is a record with lower priority value for
* the current FCF, use that record.
*/
- if (lpfc_fab_name_match(phba->fcf.fabric_name, new_fcf_record)
- && (new_fcf_record->fip_priority <
- phba->fcf.priority)) {
+ if (lpfc_fab_name_match(phba->fcf.fabric_name,
+ new_fcf_record) &&
+ (new_fcf_record->fip_priority < phba->fcf.priority)) {
/* Use this FCF record */
lpfc_copy_fcf_record(phba, new_fcf_record);
phba->fcf.addr_mode = addr_mode;
@@ -1512,6 +1634,39 @@ out:
}
/**
+ * lpfc_init_vpi_cmpl - Completion handler for init_vpi mbox command.
+ * @phba: pointer to lpfc hba data structure.
+ * @mboxq: pointer to mailbox data structure.
+ *
+ * This function handles completion of init vpi mailbox command.
+ */
+static void
+lpfc_init_vpi_cmpl(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ struct lpfc_vport *vport = mboxq->vport;
+ if (mboxq->u.mb.mbxStatus) {
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_MBOX,
+ "2609 Init VPI mailbox failed 0x%x\n",
+ mboxq->u.mb.mbxStatus);
+ mempool_free(mboxq, phba->mbox_mem_pool);
+ lpfc_vport_set_state(vport, FC_VPORT_FAILED);
+ return;
+ }
+ vport->fc_flag &= ~FC_VPORT_NEEDS_INIT_VPI;
+
+ if (phba->link_flag & LS_NPIV_FAB_SUPPORTED)
+ lpfc_initial_fdisc(vport);
+ else {
+ lpfc_vport_set_state(vport, FC_VPORT_NO_FABRIC_SUPP);
+ lpfc_printf_vlog(vport, KERN_ERR,
+ LOG_ELS,
+ "2606 No NPIV Fabric support\n");
+ }
+ return;
+}
+
+/**
* lpfc_start_fdiscs - send fdiscs for each vports on this port.
* @phba: pointer to lpfc hba data structure.
*
@@ -1523,6 +1678,8 @@ lpfc_start_fdiscs(struct lpfc_hba *phba)
{
struct lpfc_vport **vports;
int i;
+ LPFC_MBOXQ_t *mboxq;
+ int rc;
vports = lpfc_create_vport_work_array(phba);
if (vports != NULL) {
@@ -1540,6 +1697,29 @@ lpfc_start_fdiscs(struct lpfc_hba *phba)
FC_VPORT_LINKDOWN);
continue;
}
+ if (vports[i]->fc_flag & FC_VPORT_NEEDS_INIT_VPI) {
+ mboxq = mempool_alloc(phba->mbox_mem_pool,
+ GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_vlog(vports[i], KERN_ERR,
+ LOG_MBOX, "2607 Failed to allocate "
+ "init_vpi mailbox\n");
+ continue;
+ }
+ lpfc_init_vpi(phba, mboxq, vports[i]->vpi);
+ mboxq->vport = vports[i];
+ mboxq->mbox_cmpl = lpfc_init_vpi_cmpl;
+ rc = lpfc_sli_issue_mbox(phba, mboxq,
+ MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED) {
+ lpfc_printf_vlog(vports[i], KERN_ERR,
+ LOG_MBOX, "2608 Failed to issue "
+ "init_vpi mailbox\n");
+ mempool_free(mboxq,
+ phba->mbox_mem_pool);
+ }
+ continue;
+ }
if (phba->link_flag & LS_NPIV_FAB_SUPPORTED)
lpfc_initial_fdisc(vports[i]);
else {
@@ -1769,6 +1949,7 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
goto out;
}
} else {
+ vport->port_state = LPFC_VPORT_UNKNOWN;
/*
* Add the driver's default FCF record at FCF index 0 now. This
* is phase 1 implementation that support FCF index 0 and driver
@@ -1804,6 +1985,12 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, READ_LA_VAR *la)
* The driver is expected to do FIP/FCF. Call the port
* and get the FCF Table.
*/
+ spin_lock_irq(&phba->hbalock);
+ if (phba->hba_flag & FCF_DISC_INPROGRESS) {
+ spin_unlock_irq(&phba->hbalock);
+ return;
+ }
+ spin_unlock_irq(&phba->hbalock);
rc = lpfc_sli4_read_fcf_record(phba,
LPFC_FCOE_FCF_GET_FIRST);
if (rc)
@@ -2113,13 +2300,15 @@ lpfc_create_static_vport(struct lpfc_hba *phba)
LPFC_MBOXQ_t *pmb = NULL;
MAILBOX_t *mb;
struct static_vport_info *vport_info;
- int rc, i;
+ int rc = 0, i;
struct fc_vport_identifiers vport_id;
struct fc_vport *new_fc_vport;
struct Scsi_Host *shost;
struct lpfc_vport *vport;
uint16_t offset = 0;
uint8_t *vport_buff;
+ struct lpfc_dmabuf *mp;
+ uint32_t byte_count = 0;
pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!pmb) {
@@ -2142,7 +2331,9 @@ lpfc_create_static_vport(struct lpfc_hba *phba)
vport_buff = (uint8_t *) vport_info;
do {
- lpfc_dump_static_vport(phba, pmb, offset);
+ if (lpfc_dump_static_vport(phba, pmb, offset))
+ goto out;
+
pmb->vport = phba->pport;
rc = lpfc_sli_issue_mbox_wait(phba, pmb, LPFC_MBOX_TMO);
@@ -2155,17 +2346,30 @@ lpfc_create_static_vport(struct lpfc_hba *phba)
goto out;
}
- if (mb->un.varDmp.word_cnt >
- sizeof(struct static_vport_info) - offset)
- mb->un.varDmp.word_cnt =
- sizeof(struct static_vport_info) - offset;
-
- lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
- vport_buff + offset,
- mb->un.varDmp.word_cnt);
- offset += mb->un.varDmp.word_cnt;
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ byte_count = pmb->u.mqe.un.mb_words[5];
+ mp = (struct lpfc_dmabuf *) pmb->context2;
+ if (byte_count > sizeof(struct static_vport_info) -
+ offset)
+ byte_count = sizeof(struct static_vport_info)
+ - offset;
+ memcpy(vport_buff + offset, mp->virt, byte_count);
+ offset += byte_count;
+ } else {
+ if (mb->un.varDmp.word_cnt >
+ sizeof(struct static_vport_info) - offset)
+ mb->un.varDmp.word_cnt =
+ sizeof(struct static_vport_info)
+ - offset;
+ byte_count = mb->un.varDmp.word_cnt;
+ lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
+ vport_buff + offset,
+ byte_count);
+
+ offset += byte_count;
+ }
- } while (mb->un.varDmp.word_cnt &&
+ } while (byte_count &&
offset < sizeof(struct static_vport_info));
@@ -2198,7 +2402,7 @@ lpfc_create_static_vport(struct lpfc_hba *phba)
if (!new_fc_vport) {
lpfc_printf_log(phba, KERN_WARNING, LOG_INIT,
"0546 lpfc_create_static_vport failed to"
- " create vport \n");
+ " create vport\n");
continue;
}
@@ -2207,16 +2411,15 @@ lpfc_create_static_vport(struct lpfc_hba *phba)
}
out:
- /*
- * If this is timed out command, setting NULL to context2 tell SLI
- * layer not to use this buffer.
- */
- spin_lock_irq(&phba->hbalock);
- pmb->context2 = NULL;
- spin_unlock_irq(&phba->hbalock);
kfree(vport_info);
- if (rc != MBX_TIMEOUT)
+ if (rc != MBX_TIMEOUT) {
+ if (pmb->context2) {
+ mp = (struct lpfc_dmabuf *) pmb->context2;
+ lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ }
mempool_free(pmb, phba->mbox_mem_pool);
+ }
return;
}
@@ -4360,7 +4563,7 @@ lpfc_read_fcoe_param(struct lpfc_hba *phba,
fcoe_param_hdr = (struct lpfc_fip_param_hdr *)
buff;
fcoe_param = (struct lpfc_fcoe_params *)
- buff + sizeof(struct lpfc_fip_param_hdr);
+ (buff + sizeof(struct lpfc_fip_param_hdr));
if ((fcoe_param_hdr->parm_version != FIPP_VERSION) ||
(fcoe_param_hdr->length != FCOE_PARAM_LENGTH))
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index 8a3a026667e..ccb26724dc5 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -2496,8 +2496,8 @@ typedef struct {
#define DMP_VPORT_REGION_SIZE 0x200
#define DMP_MBOX_OFFSET_WORD 0x5
-#define DMP_REGION_FCOEPARAM 0x17 /* fcoe param region */
-#define DMP_FCOEPARAM_RGN_SIZE 0x400
+#define DMP_REGION_23 0x17 /* fcoe param and port state region */
+#define DMP_RGN23_SIZE 0x400
#define WAKE_UP_PARMS_REGION_ID 4
#define WAKE_UP_PARMS_WORD_SIZE 15
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index 2995d128f07..3689eee0453 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -52,6 +52,31 @@ struct dma_address {
uint32_t addr_hi;
};
+#define LPFC_SLIREV_CONF_WORD 0x58
+struct lpfc_sli_intf {
+ uint32_t word0;
+#define lpfc_sli_intf_iftype_MASK 0x00000007
+#define lpfc_sli_intf_iftype_SHIFT 0
+#define lpfc_sli_intf_iftype_WORD word0
+#define lpfc_sli_intf_rev_MASK 0x0000000f
+#define lpfc_sli_intf_rev_SHIFT 4
+#define lpfc_sli_intf_rev_WORD word0
+#define LPFC_SLIREV_CONF_SLI4 4
+#define lpfc_sli_intf_family_MASK 0x000000ff
+#define lpfc_sli_intf_family_SHIFT 8
+#define lpfc_sli_intf_family_WORD word0
+#define lpfc_sli_intf_feat1_MASK 0x000000ff
+#define lpfc_sli_intf_feat1_SHIFT 16
+#define lpfc_sli_intf_feat1_WORD word0
+#define lpfc_sli_intf_feat2_MASK 0x0000001f
+#define lpfc_sli_intf_feat2_SHIFT 24
+#define lpfc_sli_intf_feat2_WORD word0
+#define lpfc_sli_intf_valid_MASK 0x00000007
+#define lpfc_sli_intf_valid_SHIFT 29
+#define lpfc_sli_intf_valid_WORD word0
+#define LPFC_SLI_INTF_VALID 6
+};
+
#define LPFC_SLI4_BAR0 1
#define LPFC_SLI4_BAR1 2
#define LPFC_SLI4_BAR2 4
@@ -1181,6 +1206,32 @@ struct fcf_record {
#define lpfc_fcf_record_fcf_state_MASK 0x0000FFFF
#define lpfc_fcf_record_fcf_state_WORD word8
uint8_t vlan_bitmap[512];
+ uint32_t word137;
+#define lpfc_fcf_record_switch_name_0_SHIFT 0
+#define lpfc_fcf_record_switch_name_0_MASK 0x000000FF
+#define lpfc_fcf_record_switch_name_0_WORD word137
+#define lpfc_fcf_record_switch_name_1_SHIFT 8
+#define lpfc_fcf_record_switch_name_1_MASK 0x000000FF
+#define lpfc_fcf_record_switch_name_1_WORD word137
+#define lpfc_fcf_record_switch_name_2_SHIFT 16
+#define lpfc_fcf_record_switch_name_2_MASK 0x000000FF
+#define lpfc_fcf_record_switch_name_2_WORD word137
+#define lpfc_fcf_record_switch_name_3_SHIFT 24
+#define lpfc_fcf_record_switch_name_3_MASK 0x000000FF
+#define lpfc_fcf_record_switch_name_3_WORD word137
+ uint32_t word138;
+#define lpfc_fcf_record_switch_name_4_SHIFT 0
+#define lpfc_fcf_record_switch_name_4_MASK 0x000000FF
+#define lpfc_fcf_record_switch_name_4_WORD word138
+#define lpfc_fcf_record_switch_name_5_SHIFT 8
+#define lpfc_fcf_record_switch_name_5_MASK 0x000000FF
+#define lpfc_fcf_record_switch_name_5_WORD word138
+#define lpfc_fcf_record_switch_name_6_SHIFT 16
+#define lpfc_fcf_record_switch_name_6_MASK 0x000000FF
+#define lpfc_fcf_record_switch_name_6_WORD word138
+#define lpfc_fcf_record_switch_name_7_SHIFT 24
+#define lpfc_fcf_record_switch_name_7_MASK 0x000000FF
+#define lpfc_fcf_record_switch_name_7_WORD word138
};
struct lpfc_mbx_read_fcf_tbl {
@@ -1385,20 +1436,17 @@ struct lpfc_mbx_unreg_vfi {
struct lpfc_mbx_resume_rpi {
uint32_t word1;
-#define lpfc_resume_rpi_rpi_SHIFT 0
-#define lpfc_resume_rpi_rpi_MASK 0x0000FFFF
-#define lpfc_resume_rpi_rpi_WORD word1
+#define lpfc_resume_rpi_index_SHIFT 0
+#define lpfc_resume_rpi_index_MASK 0x0000FFFF
+#define lpfc_resume_rpi_index_WORD word1
+#define lpfc_resume_rpi_ii_SHIFT 30
+#define lpfc_resume_rpi_ii_MASK 0x00000003
+#define lpfc_resume_rpi_ii_WORD word1
+#define RESUME_INDEX_RPI 0
+#define RESUME_INDEX_VPI 1
+#define RESUME_INDEX_VFI 2
+#define RESUME_INDEX_FCFI 3
uint32_t event_tag;
- uint32_t word3_rsvd;
- uint32_t word4_rsvd;
- uint32_t word5_rsvd;
- uint32_t word6;
-#define lpfc_resume_rpi_vpi_SHIFT 0
-#define lpfc_resume_rpi_vpi_MASK 0x0000FFFF
-#define lpfc_resume_rpi_vpi_WORD word6
-#define lpfc_resume_rpi_vfi_SHIFT 16
-#define lpfc_resume_rpi_vfi_MASK 0x0000FFFF
-#define lpfc_resume_rpi_vfi_WORD word6
};
#define REG_FCF_INVALID_QID 0xFFFF
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index fc67cc65c63..562d8cee874 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -211,7 +211,7 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
goto out_free_mbox;
do {
- lpfc_dump_mem(phba, pmb, offset);
+ lpfc_dump_mem(phba, pmb, offset, DMP_REGION_VPD);
rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
if (rc != MBX_SUCCESS) {
@@ -425,6 +425,9 @@ lpfc_config_port_post(struct lpfc_hba *phba)
return -EIO;
}
+ /* Check if the port is disabled */
+ lpfc_sli_read_link_ste(phba);
+
/* Reset the DFT_HBA_Q_DEPTH to the max xri */
if (phba->cfg_hba_queue_depth > (mb->un.varRdConfig.max_xri+1))
phba->cfg_hba_queue_depth =
@@ -524,27 +527,46 @@ lpfc_config_port_post(struct lpfc_hba *phba)
/* Set up error attention (ERATT) polling timer */
mod_timer(&phba->eratt_poll, jiffies + HZ * LPFC_ERATT_POLL_INTERVAL);
- lpfc_init_link(phba, pmb, phba->cfg_topology, phba->cfg_link_speed);
- pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
- lpfc_set_loopback_flag(phba);
- rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
- if (rc != MBX_SUCCESS) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ if (phba->hba_flag & LINK_DISABLED) {
+ lpfc_printf_log(phba,
+ KERN_ERR, LOG_INIT,
+ "2598 Adapter Link is disabled.\n");
+ lpfc_down_link(phba, pmb);
+ pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+ if ((rc != MBX_SUCCESS) && (rc != MBX_BUSY)) {
+ lpfc_printf_log(phba,
+ KERN_ERR, LOG_INIT,
+ "2599 Adapter failed to issue DOWN_LINK"
+ " mbox command rc 0x%x\n", rc);
+
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return -EIO;
+ }
+ } else {
+ lpfc_init_link(phba, pmb, phba->cfg_topology,
+ phba->cfg_link_speed);
+ pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ lpfc_set_loopback_flag(phba);
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0454 Adapter failed to init, mbxCmd x%x "
"INIT_LINK, mbxStatus x%x\n",
mb->mbxCommand, mb->mbxStatus);
- /* Clear all interrupt enable conditions */
- writel(0, phba->HCregaddr);
- readl(phba->HCregaddr); /* flush */
- /* Clear all pending interrupts */
- writel(0xffffffff, phba->HAregaddr);
- readl(phba->HAregaddr); /* flush */
+ /* Clear all interrupt enable conditions */
+ writel(0, phba->HCregaddr);
+ readl(phba->HCregaddr); /* flush */
+ /* Clear all pending interrupts */
+ writel(0xffffffff, phba->HAregaddr);
+ readl(phba->HAregaddr); /* flush */
- phba->link_state = LPFC_HBA_ERROR;
- if (rc != MBX_BUSY)
- mempool_free(pmb, phba->mbox_mem_pool);
- return -EIO;
+ phba->link_state = LPFC_HBA_ERROR;
+ if (rc != MBX_BUSY)
+ mempool_free(pmb, phba->mbox_mem_pool);
+ return -EIO;
+ }
}
/* MBOX buffer will be freed in mbox compl */
pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
@@ -558,7 +580,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
KERN_ERR,
LOG_INIT,
"0456 Adapter failed to issue "
- "ASYNCEVT_ENABLE mbox status x%x \n.",
+ "ASYNCEVT_ENABLE mbox status x%x\n",
rc);
mempool_free(pmb, phba->mbox_mem_pool);
}
@@ -572,7 +594,7 @@ lpfc_config_port_post(struct lpfc_hba *phba)
if ((rc != MBX_BUSY) && (rc != MBX_SUCCESS)) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "0435 Adapter failed "
- "to get Option ROM version status x%x\n.", rc);
+ "to get Option ROM version status x%x\n", rc);
mempool_free(pmb, phba->mbox_mem_pool);
}
@@ -2133,6 +2155,8 @@ lpfc_online(struct lpfc_hba *phba)
vports[i]->fc_flag &= ~FC_OFFLINE_MODE;
if (phba->sli3_options & LPFC_SLI3_NPIV_ENABLED)
vports[i]->fc_flag |= FC_VPORT_NEEDS_REG_VPI;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ vports[i]->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
spin_unlock_irq(shost->host_lock);
}
lpfc_destroy_vport_work_array(phba, vports);
@@ -2807,6 +2831,7 @@ lpfc_sli4_async_link_evt(struct lpfc_hba *phba,
att_type = lpfc_sli4_parse_latt_type(phba, acqe_link);
if (att_type != AT_LINK_DOWN && att_type != AT_LINK_UP)
return;
+ phba->fcoe_eventtag = acqe_link->event_tag;
pmb = (LPFC_MBOXQ_t *)mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!pmb) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
@@ -2894,18 +2919,20 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
uint8_t event_type = bf_get(lpfc_acqe_fcoe_event_type, acqe_fcoe);
int rc;
+ phba->fcoe_eventtag = acqe_fcoe->event_tag;
switch (event_type) {
case LPFC_FCOE_EVENT_TYPE_NEW_FCF:
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
- "2546 New FCF found index 0x%x tag 0x%x \n",
+ "2546 New FCF found index 0x%x tag 0x%x\n",
acqe_fcoe->fcf_index,
acqe_fcoe->event_tag);
/*
- * If the current FCF is in discovered state,
- * do nothing.
+ * If the current FCF is in discovered state, or
+ * FCF discovery is in progress do nothing.
*/
spin_lock_irq(&phba->hbalock);
- if (phba->fcf.fcf_flag & FCF_DISCOVERED) {
+ if ((phba->fcf.fcf_flag & FCF_DISCOVERED) ||
+ (phba->hba_flag & FCF_DISC_INPROGRESS)) {
spin_unlock_irq(&phba->hbalock);
break;
}
@@ -2922,7 +2949,7 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
case LPFC_FCOE_EVENT_TYPE_FCF_TABLE_FULL:
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "2548 FCF Table full count 0x%x tag 0x%x \n",
+ "2548 FCF Table full count 0x%x tag 0x%x\n",
bf_get(lpfc_acqe_fcoe_fcf_count, acqe_fcoe),
acqe_fcoe->event_tag);
break;
@@ -2930,7 +2957,7 @@ lpfc_sli4_async_fcoe_evt(struct lpfc_hba *phba,
case LPFC_FCOE_EVENT_TYPE_FCF_DEAD:
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
"2549 FCF disconnected fron network index 0x%x"
- " tag 0x%x \n", acqe_fcoe->fcf_index,
+ " tag 0x%x\n", acqe_fcoe->fcf_index,
acqe_fcoe->event_tag);
/* If the event is not for currently used fcf do nothing */
if (phba->fcf.fcf_indx != acqe_fcoe->fcf_index)
@@ -4130,8 +4157,7 @@ lpfc_hba_alloc(struct pci_dev *pdev)
/* Allocate memory for HBA structure */
phba = kzalloc(sizeof(struct lpfc_hba), GFP_KERNEL);
if (!phba) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1417 Failed to allocate hba struct.\n");
+ dev_err(&pdev->dev, "failed to allocate hba struct\n");
return NULL;
}
@@ -4145,6 +4171,9 @@ lpfc_hba_alloc(struct pci_dev *pdev)
return NULL;
}
+ mutex_init(&phba->ct_event_mutex);
+ INIT_LIST_HEAD(&phba->ct_ev_waiters);
+
return phba;
}
@@ -4489,23 +4518,6 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba)
if (!phba->sli4_hba.STAregaddr)
return -ENODEV;
- /* With uncoverable error, log the error message and return error */
- onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr);
- onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr);
- if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) {
- uerrlo_reg.word0 = readl(phba->sli4_hba.UERRLOregaddr);
- uerrhi_reg.word0 = readl(phba->sli4_hba.UERRHIregaddr);
- if (uerrlo_reg.word0 || uerrhi_reg.word0) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1422 HBA Unrecoverable error: "
- "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
- "online0_reg=0x%x, online1_reg=0x%x\n",
- uerrlo_reg.word0, uerrhi_reg.word0,
- onlnreg0, onlnreg1);
- }
- return -ENODEV;
- }
-
/* Wait up to 30 seconds for the SLI Port POST done and ready */
for (i = 0; i < 3000; i++) {
sta_reg.word0 = readl(phba->sli4_hba.STAregaddr);
@@ -4545,6 +4557,23 @@ lpfc_sli4_post_status_check(struct lpfc_hba *phba)
bf_get(lpfc_scratchpad_featurelevel1, &scratchpad),
bf_get(lpfc_scratchpad_featurelevel2, &scratchpad));
+ /* With uncoverable error, log the error message and return error */
+ onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr);
+ onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr);
+ if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) {
+ uerrlo_reg.word0 = readl(phba->sli4_hba.UERRLOregaddr);
+ uerrhi_reg.word0 = readl(phba->sli4_hba.UERRHIregaddr);
+ if (uerrlo_reg.word0 || uerrhi_reg.word0) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1422 HBA Unrecoverable error: "
+ "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
+ "online0_reg=0x%x, online1_reg=0x%x\n",
+ uerrlo_reg.word0, uerrhi_reg.word0,
+ onlnreg0, onlnreg1);
+ }
+ return -ENODEV;
+ }
+
return port_error;
}
@@ -7347,6 +7376,9 @@ lpfc_pci_probe_one_s4(struct pci_dev *pdev, const struct pci_device_id *pid)
/* Perform post initialization setup */
lpfc_post_init_setup(phba);
+ /* Check if there are static vports to be created. */
+ lpfc_create_static_vport(phba);
+
return 0;
out_disable_intr:
@@ -7636,19 +7668,17 @@ static int __devinit
lpfc_pci_probe_one(struct pci_dev *pdev, const struct pci_device_id *pid)
{
int rc;
- uint16_t dev_id;
+ struct lpfc_sli_intf intf;
- if (pci_read_config_word(pdev, PCI_DEVICE_ID, &dev_id))
+ if (pci_read_config_dword(pdev, LPFC_SLIREV_CONF_WORD, &intf.word0))
return -ENODEV;
- switch (dev_id) {
- case PCI_DEVICE_ID_TIGERSHARK:
+ if ((bf_get(lpfc_sli_intf_valid, &intf) == LPFC_SLI_INTF_VALID) &&
+ (bf_get(lpfc_sli_intf_rev, &intf) == LPFC_SLIREV_CONF_SLI4))
rc = lpfc_pci_probe_one_s4(pdev, pid);
- break;
- default:
+ else
rc = lpfc_pci_probe_one_s3(pdev, pid);
- break;
- }
+
return rc;
}
diff --git a/drivers/scsi/lpfc/lpfc_mbox.c b/drivers/scsi/lpfc/lpfc_mbox.c
index 3423571dd1b..1ab405902a1 100644
--- a/drivers/scsi/lpfc/lpfc_mbox.c
+++ b/drivers/scsi/lpfc/lpfc_mbox.c
@@ -52,48 +52,85 @@
* This routine prepares the mailbox command for dumping list of static
* vports to be created.
**/
-void
+int
lpfc_dump_static_vport(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb,
uint16_t offset)
{
MAILBOX_t *mb;
- void *ctx;
+ struct lpfc_dmabuf *mp;
mb = &pmb->u.mb;
- ctx = pmb->context2;
/* Setup to dump vport info region */
memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
mb->mbxCommand = MBX_DUMP_MEMORY;
- mb->un.varDmp.cv = 1;
mb->un.varDmp.type = DMP_NV_PARAMS;
mb->un.varDmp.entry_index = offset;
mb->un.varDmp.region_id = DMP_REGION_VPORT;
- mb->un.varDmp.word_cnt = DMP_RSP_SIZE/sizeof(uint32_t);
- mb->un.varDmp.co = 0;
- mb->un.varDmp.resp_offset = 0;
- pmb->context2 = ctx;
mb->mbxOwner = OWN_HOST;
- return;
+ /* For SLI3 HBAs data is embedded in mailbox */
+ if (phba->sli_rev != LPFC_SLI_REV4) {
+ mb->un.varDmp.cv = 1;
+ mb->un.varDmp.word_cnt = DMP_RSP_SIZE/sizeof(uint32_t);
+ return 0;
+ }
+
+ /* For SLI4 HBAs driver need to allocate memory */
+ mp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL);
+ if (mp)
+ mp->virt = lpfc_mbuf_alloc(phba, 0, &mp->phys);
+
+ if (!mp || !mp->virt) {
+ kfree(mp);
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX,
+ "2605 lpfc_dump_static_vport: memory"
+ " allocation failed\n");
+ return 1;
+ }
+ memset(mp->virt, 0, LPFC_BPL_SIZE);
+ INIT_LIST_HEAD(&mp->list);
+ /* save address for completion */
+ pmb->context2 = (uint8_t *) mp;
+ mb->un.varWords[3] = putPaddrLow(mp->phys);
+ mb->un.varWords[4] = putPaddrHigh(mp->phys);
+ mb->un.varDmp.sli4_length = sizeof(struct static_vport_info);
+
+ return 0;
+}
+
+/**
+ * lpfc_down_link - Bring down HBAs link.
+ * @phba: pointer to lpfc hba data structure.
+ * @pmb: pointer to the driver internal queue element for mailbox command.
+ *
+ * This routine prepares a mailbox command to bring down HBA link.
+ **/
+void
+lpfc_down_link(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb)
+{
+ MAILBOX_t *mb;
+ memset(pmb, 0, sizeof(LPFC_MBOXQ_t));
+ mb = &pmb->u.mb;
+ mb->mbxCommand = MBX_DOWN_LINK;
+ mb->mbxOwner = OWN_HOST;
}
/**
- * lpfc_dump_mem - Prepare a mailbox command for retrieving HBA's VPD memory
+ * lpfc_dump_mem - Prepare a mailbox command for reading a region.
* @phba: pointer to lpfc hba data structure.
* @pmb: pointer to the driver internal queue element for mailbox command.
- * @offset: offset for dumping VPD memory mailbox command.
+ * @offset: offset into the region.
+ * @region_id: config region id.
*
* The dump mailbox command provides a method for the device driver to obtain
* various types of information from the HBA device.
*
- * This routine prepares the mailbox command for dumping HBA Vital Product
- * Data (VPD) memory. This mailbox command is to be used for retrieving a
- * portion (DMP_RSP_SIZE bytes) of a HBA's VPD from the HBA at an address
- * offset specified by the offset parameter.
+ * This routine prepares the mailbox command for dumping HBA's config region.
**/
void
-lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint16_t offset)
+lpfc_dump_mem(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb, uint16_t offset,
+ uint16_t region_id)
{
MAILBOX_t *mb;
void *ctx;
@@ -107,7 +144,7 @@ lpfc_dump_mem(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb, uint16_t offset)
mb->un.varDmp.cv = 1;
mb->un.varDmp.type = DMP_NV_PARAMS;
mb->un.varDmp.entry_index = offset;
- mb->un.varDmp.region_id = DMP_REGION_VPD;
+ mb->un.varDmp.region_id = region_id;
mb->un.varDmp.word_cnt = (DMP_RSP_SIZE / sizeof (uint32_t));
mb->un.varDmp.co = 0;
mb->un.varDmp.resp_offset = 0;
@@ -1789,6 +1826,7 @@ lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys)
/**
* lpfc_init_vpi - Initialize the INIT_VPI mailbox command
+ * @phba: pointer to the hba structure to init the VPI for.
* @mbox: pointer to lpfc mbox command to initialize.
* @vpi: VPI to be initialized.
*
@@ -1799,11 +1837,14 @@ lpfc_reg_vfi(struct lpfcMboxq *mbox, struct lpfc_vport *vport, dma_addr_t phys)
* successful virtual NPort login.
**/
void
-lpfc_init_vpi(struct lpfcMboxq *mbox, uint16_t vpi)
+lpfc_init_vpi(struct lpfc_hba *phba, struct lpfcMboxq *mbox, uint16_t vpi)
{
memset(mbox, 0, sizeof(*mbox));
bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_INIT_VPI);
- bf_set(lpfc_init_vpi_vpi, &mbox->u.mqe.un.init_vpi, vpi);
+ bf_set(lpfc_init_vpi_vpi, &mbox->u.mqe.un.init_vpi,
+ vpi + phba->vpi_base);
+ bf_set(lpfc_init_vpi_vfi, &mbox->u.mqe.un.init_vpi,
+ phba->pport->vfi + phba->vfi_base);
}
/**
@@ -1852,7 +1893,7 @@ lpfc_dump_fcoe_param(struct lpfc_hba *phba,
/* dump_fcoe_param failed to allocate memory */
lpfc_printf_log(phba, KERN_WARNING, LOG_MBOX,
"2569 lpfc_dump_fcoe_param: memory"
- " allocation failed \n");
+ " allocation failed\n");
return 1;
}
@@ -1864,8 +1905,8 @@ lpfc_dump_fcoe_param(struct lpfc_hba *phba,
mb->mbxCommand = MBX_DUMP_MEMORY;
mb->un.varDmp.type = DMP_NV_PARAMS;
- mb->un.varDmp.region_id = DMP_REGION_FCOEPARAM;
- mb->un.varDmp.sli4_length = DMP_FCOEPARAM_RGN_SIZE;
+ mb->un.varDmp.region_id = DMP_REGION_23;
+ mb->un.varDmp.sli4_length = DMP_RGN23_SIZE;
mb->un.varWords[3] = putPaddrLow(mp->phys);
mb->un.varWords[4] = putPaddrHigh(mp->phys);
return 0;
@@ -1938,9 +1979,7 @@ lpfc_resume_rpi(struct lpfcMboxq *mbox, struct lpfc_nodelist *ndlp)
memset(mbox, 0, sizeof(*mbox));
resume_rpi = &mbox->u.mqe.un.resume_rpi;
bf_set(lpfc_mqe_command, &mbox->u.mqe, MBX_RESUME_RPI);
- bf_set(lpfc_resume_rpi_rpi, resume_rpi, ndlp->nlp_rpi);
- bf_set(lpfc_resume_rpi_vpi, resume_rpi,
- ndlp->vport->vpi + ndlp->vport->phba->vpi_base);
- bf_set(lpfc_resume_rpi_vfi, resume_rpi,
- ndlp->vport->vfi + ndlp->vport->phba->vfi_base);
+ bf_set(lpfc_resume_rpi_index, resume_rpi, ndlp->nlp_rpi);
+ bf_set(lpfc_resume_rpi_ii, resume_rpi, RESUME_INDEX_RPI);
+ resume_rpi->event_tag = ndlp->phba->fc_eventTag;
}
diff --git a/drivers/scsi/lpfc/lpfc_mem.c b/drivers/scsi/lpfc/lpfc_mem.c
index e198c917c13..a1b6db6016d 100644
--- a/drivers/scsi/lpfc/lpfc_mem.c
+++ b/drivers/scsi/lpfc/lpfc_mem.c
@@ -110,17 +110,28 @@ lpfc_mem_alloc(struct lpfc_hba *phba, int align)
sizeof(struct lpfc_nodelist));
if (!phba->nlp_mem_pool)
goto fail_free_mbox_pool;
- phba->lpfc_hrb_pool = pci_pool_create("lpfc_hrb_pool",
+
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ phba->lpfc_hrb_pool = pci_pool_create("lpfc_hrb_pool",
phba->pcidev,
LPFC_HDR_BUF_SIZE, align, 0);
- if (!phba->lpfc_hrb_pool)
- goto fail_free_nlp_mem_pool;
- phba->lpfc_drb_pool = pci_pool_create("lpfc_drb_pool",
+ if (!phba->lpfc_hrb_pool)
+ goto fail_free_nlp_mem_pool;
+
+ phba->lpfc_drb_pool = pci_pool_create("lpfc_drb_pool",
phba->pcidev,
LPFC_DATA_BUF_SIZE, align, 0);
- if (!phba->lpfc_drb_pool)
- goto fail_free_hbq_pool;
-
+ if (!phba->lpfc_drb_pool)
+ goto fail_free_hrb_pool;
+ phba->lpfc_hbq_pool = NULL;
+ } else {
+ phba->lpfc_hbq_pool = pci_pool_create("lpfc_hbq_pool",
+ phba->pcidev, LPFC_BPL_SIZE, align, 0);
+ if (!phba->lpfc_hbq_pool)
+ goto fail_free_nlp_mem_pool;
+ phba->lpfc_hrb_pool = NULL;
+ phba->lpfc_drb_pool = NULL;
+ }
/* vpi zero is reserved for the physical port so add 1 to max */
longs = ((phba->max_vpi + 1) + BITS_PER_LONG - 1) / BITS_PER_LONG;
phba->vpi_bmask = kzalloc(longs * sizeof(unsigned long), GFP_KERNEL);
@@ -132,7 +143,7 @@ lpfc_mem_alloc(struct lpfc_hba *phba, int align)
fail_free_dbq_pool:
pci_pool_destroy(phba->lpfc_drb_pool);
phba->lpfc_drb_pool = NULL;
- fail_free_hbq_pool:
+ fail_free_hrb_pool:
pci_pool_destroy(phba->lpfc_hrb_pool);
phba->lpfc_hrb_pool = NULL;
fail_free_nlp_mem_pool:
@@ -176,11 +187,17 @@ lpfc_mem_free(struct lpfc_hba *phba)
/* Free HBQ pools */
lpfc_sli_hbqbuf_free_all(phba);
- pci_pool_destroy(phba->lpfc_drb_pool);
+ if (phba->lpfc_drb_pool)
+ pci_pool_destroy(phba->lpfc_drb_pool);
phba->lpfc_drb_pool = NULL;
- pci_pool_destroy(phba->lpfc_hrb_pool);
+ if (phba->lpfc_hrb_pool)
+ pci_pool_destroy(phba->lpfc_hrb_pool);
phba->lpfc_hrb_pool = NULL;
+ if (phba->lpfc_hbq_pool)
+ pci_pool_destroy(phba->lpfc_hbq_pool);
+ phba->lpfc_hbq_pool = NULL;
+
/* Free NLP memory pool */
mempool_destroy(phba->nlp_mem_pool);
phba->nlp_mem_pool = NULL;
@@ -380,7 +397,7 @@ lpfc_els_hbq_alloc(struct lpfc_hba *phba)
if (!hbqbp)
return NULL;
- hbqbp->dbuf.virt = pci_pool_alloc(phba->lpfc_hrb_pool, GFP_KERNEL,
+ hbqbp->dbuf.virt = pci_pool_alloc(phba->lpfc_hbq_pool, GFP_KERNEL,
&hbqbp->dbuf.phys);
if (!hbqbp->dbuf.virt) {
kfree(hbqbp);
@@ -405,7 +422,7 @@ lpfc_els_hbq_alloc(struct lpfc_hba *phba)
void
lpfc_els_hbq_free(struct lpfc_hba *phba, struct hbq_dmabuf *hbqbp)
{
- pci_pool_free(phba->lpfc_hrb_pool, hbqbp->dbuf.virt, hbqbp->dbuf.phys);
+ pci_pool_free(phba->lpfc_hbq_pool, hbqbp->dbuf.virt, hbqbp->dbuf.phys);
kfree(hbqbp);
return;
}
diff --git a/drivers/scsi/lpfc/lpfc_nl.h b/drivers/scsi/lpfc/lpfc_nl.h
index 27d1a88a98f..d655ed3eebe 100644
--- a/drivers/scsi/lpfc/lpfc_nl.h
+++ b/drivers/scsi/lpfc/lpfc_nl.h
@@ -177,3 +177,23 @@ struct temp_event {
uint32_t data;
};
+/* bsg definitions */
+#define LPFC_BSG_VENDOR_SET_CT_EVENT 1
+#define LPFC_BSG_VENDOR_GET_CT_EVENT 2
+
+struct set_ct_event {
+ uint32_t command;
+ uint32_t ev_req_id;
+ uint32_t ev_reg_id;
+};
+
+struct get_ct_event {
+ uint32_t command;
+ uint32_t ev_reg_id;
+ uint32_t ev_req_id;
+};
+
+struct get_ct_event_reply {
+ uint32_t immed_data;
+ uint32_t type;
+};
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index da59c4f0168..61d08970380 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.c
@@ -2142,7 +2142,7 @@ lpfc_handle_fcp_err(struct lpfc_vport *vport, struct lpfc_scsi_buf *lpfc_cmd,
} else if (resp_info & RESID_OVER) {
lpfc_printf_vlog(vport, KERN_WARNING, LOG_FCP,
"9028 FCP command x%x residual overrun error. "
- "Data: x%x x%x \n", cmnd->cmnd[0],
+ "Data: x%x x%x\n", cmnd->cmnd[0],
scsi_bufflen(cmnd), scsi_get_resid(cmnd));
host_status = DID_ERROR;
@@ -2843,7 +2843,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
dif_op_str[scsi_get_prot_op(cmnd)]);
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9034 BLKGRD: CDB: %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x \n",
+ "%02x %02x %02x %02x %02x\n",
cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
@@ -2871,7 +2871,7 @@ lpfc_queuecommand(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
dif_op_str[scsi_get_prot_op(cmnd)]);
lpfc_printf_vlog(vport, KERN_WARNING, LOG_BG,
"9039 BLKGRD: CDB: %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x \n",
+ "%02x %02x %02x %02x %02x\n",
cmnd->cmnd[0], cmnd->cmnd[1], cmnd->cmnd[2],
cmnd->cmnd[3], cmnd->cmnd[4], cmnd->cmnd[5],
cmnd->cmnd[6], cmnd->cmnd[7], cmnd->cmnd[8],
@@ -3584,6 +3584,7 @@ struct scsi_host_template lpfc_template = {
.use_clustering = ENABLE_CLUSTERING,
.shost_attrs = lpfc_hba_attrs,
.max_sectors = 0xFFFF,
+ .vendor_id = LPFC_NL_VENDOR_ID,
};
struct scsi_host_template lpfc_vport_template = {
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index acc43b061ba..43cbe336f1f 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -4139,7 +4139,7 @@ lpfc_sli4_read_fcoe_params(struct lpfc_hba *phba,
return -EIO;
}
data_length = mqe->un.mb_words[5];
- if (data_length > DMP_FCOEPARAM_RGN_SIZE) {
+ if (data_length > DMP_RGN23_SIZE) {
lpfc_mbuf_free(phba, mp->virt, mp->phys);
kfree(mp);
return -EIO;
@@ -4304,7 +4304,7 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
*/
if (lpfc_sli4_read_fcoe_params(phba, mboxq))
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_INIT,
- "2570 Failed to read FCoE parameters \n");
+ "2570 Failed to read FCoE parameters\n");
/* Issue READ_REV to collect vpd and FW information. */
vpd_size = PAGE_SIZE;
@@ -4522,12 +4522,8 @@ lpfc_sli4_hba_setup(struct lpfc_hba *phba)
lpfc_sli4_rb_setup(phba);
/* Start the ELS watchdog timer */
- /*
- * The driver for SLI4 is not yet ready to process timeouts
- * or interrupts. Once it is, the comment bars can be removed.
- */
- /* mod_timer(&vport->els_tmofunc,
- * jiffies + HZ * (phba->fc_ratov*2)); */
+ mod_timer(&vport->els_tmofunc,
+ jiffies + HZ * (phba->fc_ratov * 2));
/* Start heart beat timer */
mod_timer(&phba->hb_tmofunc,
@@ -4706,13 +4702,13 @@ lpfc_sli_issue_mbox_s3(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmbox,
spin_lock_irqsave(&phba->hbalock, drvr_flag);
if (!pmbox) {
+ phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
/* processing mbox queue from intr_handler */
if (unlikely(psli->sli_flag & LPFC_SLI_ASYNC_MBX_BLK)) {
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
return MBX_SUCCESS;
}
processing_queue = 1;
- phba->sli.sli_flag &= ~LPFC_SLI_MBOX_ACTIVE;
pmbox = lpfc_mbox_get(phba);
if (!pmbox) {
spin_unlock_irqrestore(&phba->hbalock, drvr_flag);
@@ -5279,6 +5275,18 @@ lpfc_sli_issue_mbox_s4(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
unsigned long iflags;
int rc;
+ rc = lpfc_mbox_dev_check(phba);
+ if (unlikely(rc)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
+ "(%d):2544 Mailbox command x%x (x%x) "
+ "cannot issue Data: x%x x%x\n",
+ mboxq->vport ? mboxq->vport->vpi : 0,
+ mboxq->u.mb.mbxCommand,
+ lpfc_sli4_mbox_opcode_get(phba, mboxq),
+ psli->sli_flag, flag);
+ goto out_not_finished;
+ }
+
/* Detect polling mode and jump to a handler */
if (!phba->sli4_hba.intr_enable) {
if (flag == MBX_POLL)
@@ -5338,17 +5346,6 @@ lpfc_sli_issue_mbox_s4(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
psli->sli_flag, flag);
goto out_not_finished;
}
- rc = lpfc_mbox_dev_check(phba);
- if (unlikely(rc)) {
- lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
- "(%d):2544 Mailbox command x%x (x%x) "
- "cannot issue Data: x%x x%x\n",
- mboxq->vport ? mboxq->vport->vpi : 0,
- mboxq->u.mb.mbxCommand,
- lpfc_sli4_mbox_opcode_get(phba, mboxq),
- psli->sli_flag, flag);
- goto out_not_finished;
- }
/* Put the mailbox command to the driver internal FIFO */
psli->slistat.mbox_busy++;
@@ -5817,19 +5814,21 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
/**
* lpfc_sli4_scmd_to_wqidx_distr - scsi command to SLI4 WQ index distribution
* @phba: Pointer to HBA context object.
- * @piocb: Pointer to command iocb.
*
* This routine performs a round robin SCSI command to SLI4 FCP WQ index
- * distribution.
+ * distribution. This is called by __lpfc_sli_issue_iocb_s4() with the hbalock
+ * held.
*
* Return: index into SLI4 fast-path FCP queue index.
**/
static uint32_t
-lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba, struct lpfc_iocbq *piocb)
+lpfc_sli4_scmd_to_wqidx_distr(struct lpfc_hba *phba)
{
- static uint32_t fcp_qidx;
+ ++phba->fcp_qidx;
+ if (phba->fcp_qidx >= phba->cfg_fcp_wq_count)
+ phba->fcp_qidx = 0;
- return fcp_qidx++ % phba->cfg_fcp_wq_count;
+ return phba->fcp_qidx;
}
/**
@@ -6156,7 +6155,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
return IOCB_ERROR;
if (piocb->iocb_flag & LPFC_IO_FCP) {
- fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba, piocb);
+ fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba);
if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[fcp_wqidx], &wqe))
return IOCB_ERROR;
} else {
@@ -6327,7 +6326,7 @@ lpfc_sli_async_event_handler(struct lpfc_hba * phba,
KERN_ERR,
LOG_SLI,
"0346 Ring %d handler: unexpected ASYNC_STATUS"
- " evt_code 0x%x \n"
+ " evt_code 0x%x\n"
"W0 0x%08x W1 0x%08x W2 0x%08x W3 0x%08x\n"
"W4 0x%08x W5 0x%08x W6 0x%08x W7 0x%08x\n"
"W8 0x%08x W9 0x%08x W10 0x%08x W11 0x%08x\n"
@@ -6790,6 +6789,33 @@ lpfc_sli_pcimem_bcopy(void *srcp, void *destp, uint32_t cnt)
/**
+ * lpfc_sli_bemem_bcopy - SLI memory copy function
+ * @srcp: Source memory pointer.
+ * @destp: Destination memory pointer.
+ * @cnt: Number of words required to be copied.
+ *
+ * This function is used for copying data between a data structure
+ * with big endian representation to local endianness.
+ * This function can be called with or without lock.
+ **/
+void
+lpfc_sli_bemem_bcopy(void *srcp, void *destp, uint32_t cnt)
+{
+ uint32_t *src = srcp;
+ uint32_t *dest = destp;
+ uint32_t ldata;
+ int i;
+
+ for (i = 0; i < (int)cnt; i += sizeof(uint32_t)) {
+ ldata = *src;
+ ldata = be32_to_cpu(ldata);
+ *dest = ldata;
+ src++;
+ dest++;
+ }
+}
+
+/**
* lpfc_sli_ringpostbuf_put - Function to add a buffer to postbufq
* @phba: Pointer to HBA context object.
* @pring: Pointer to driver SLI ring object.
@@ -7678,12 +7704,6 @@ lpfc_sli4_eratt_read(struct lpfc_hba *phba)
"online0_reg=0x%x, online1_reg=0x%x\n",
uerr_sta_lo, uerr_sta_hi,
onlnreg0, onlnreg1);
- /* TEMP: as the driver error recover logic is not
- * fully developed, we just log the error message
- * and the device error attention action is now
- * temporarily disabled.
- */
- return 0;
phba->work_status[0] = uerr_sta_lo;
phba->work_status[1] = uerr_sta_hi;
/* Set the driver HA work bitmap */
@@ -9499,8 +9519,7 @@ lpfc_eq_create(struct lpfc_hba *phba, struct lpfc_queue *eq, uint16_t imax)
eq->host_index = 0;
eq->hba_index = 0;
- if (rc != MBX_TIMEOUT)
- mempool_free(mbox, phba->mbox_mem_pool);
+ mempool_free(mbox, phba->mbox_mem_pool);
return status;
}
@@ -9604,10 +9623,9 @@ lpfc_cq_create(struct lpfc_hba *phba, struct lpfc_queue *cq,
cq->queue_id = bf_get(lpfc_mbx_cq_create_q_id, &cq_create->u.response);
cq->host_index = 0;
cq->hba_index = 0;
-out:
- if (rc != MBX_TIMEOUT)
- mempool_free(mbox, phba->mbox_mem_pool);
+out:
+ mempool_free(mbox, phba->mbox_mem_pool);
return status;
}
@@ -9712,8 +9730,7 @@ lpfc_mq_create(struct lpfc_hba *phba, struct lpfc_queue *mq,
/* link the mq onto the parent cq child list */
list_add_tail(&mq->list, &cq->child_list);
out:
- if (rc != MBX_TIMEOUT)
- mempool_free(mbox, phba->mbox_mem_pool);
+ mempool_free(mbox, phba->mbox_mem_pool);
return status;
}
@@ -9795,8 +9812,7 @@ lpfc_wq_create(struct lpfc_hba *phba, struct lpfc_queue *wq,
/* link the wq onto the parent cq child list */
list_add_tail(&wq->list, &cq->child_list);
out:
- if (rc != MBX_TIMEOUT)
- mempool_free(mbox, phba->mbox_mem_pool);
+ mempool_free(mbox, phba->mbox_mem_pool);
return status;
}
@@ -9970,8 +9986,7 @@ lpfc_rq_create(struct lpfc_hba *phba, struct lpfc_queue *hrq,
list_add_tail(&drq->list, &cq->child_list);
out:
- if (rc != MBX_TIMEOUT)
- mempool_free(mbox, phba->mbox_mem_pool);
+ mempool_free(mbox, phba->mbox_mem_pool);
return status;
}
@@ -10026,8 +10041,7 @@ lpfc_eq_destroy(struct lpfc_hba *phba, struct lpfc_queue *eq)
/* Remove eq from any list */
list_del_init(&eq->list);
- if (rc != MBX_TIMEOUT)
- mempool_free(mbox, eq->phba->mbox_mem_pool);
+ mempool_free(mbox, eq->phba->mbox_mem_pool);
return status;
}
@@ -10080,8 +10094,7 @@ lpfc_cq_destroy(struct lpfc_hba *phba, struct lpfc_queue *cq)
}
/* Remove cq from any list */
list_del_init(&cq->list);
- if (rc != MBX_TIMEOUT)
- mempool_free(mbox, cq->phba->mbox_mem_pool);
+ mempool_free(mbox, cq->phba->mbox_mem_pool);
return status;
}
@@ -10134,8 +10147,7 @@ lpfc_mq_destroy(struct lpfc_hba *phba, struct lpfc_queue *mq)
}
/* Remove mq from any list */
list_del_init(&mq->list);
- if (rc != MBX_TIMEOUT)
- mempool_free(mbox, mq->phba->mbox_mem_pool);
+ mempool_free(mbox, mq->phba->mbox_mem_pool);
return status;
}
@@ -10187,8 +10199,7 @@ lpfc_wq_destroy(struct lpfc_hba *phba, struct lpfc_queue *wq)
}
/* Remove wq from any list */
list_del_init(&wq->list);
- if (rc != MBX_TIMEOUT)
- mempool_free(mbox, wq->phba->mbox_mem_pool);
+ mempool_free(mbox, wq->phba->mbox_mem_pool);
return status;
}
@@ -10258,8 +10269,7 @@ lpfc_rq_destroy(struct lpfc_hba *phba, struct lpfc_queue *hrq,
}
list_del_init(&hrq->list);
list_del_init(&drq->list);
- if (rc != MBX_TIMEOUT)
- mempool_free(mbox, hrq->phba->mbox_mem_pool);
+ mempool_free(mbox, hrq->phba->mbox_mem_pool);
return status;
}
@@ -10933,6 +10943,7 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
first_iocbq = lpfc_sli_get_iocbq(vport->phba);
if (first_iocbq) {
/* Initialize the first IOCB. */
+ first_iocbq->iocb.unsli3.rcvsli3.acc_len = 0;
first_iocbq->iocb.ulpStatus = IOSTAT_SUCCESS;
first_iocbq->iocb.ulpCommand = CMD_IOCB_RCV_SEQ64_CX;
first_iocbq->iocb.ulpContext = be16_to_cpu(fc_hdr->fh_ox_id);
@@ -10945,6 +10956,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
first_iocbq->iocb.un.cont64[0].tus.f.bdeSize =
LPFC_DATA_BUF_SIZE;
first_iocbq->iocb.un.rcvels.remoteID = sid;
+ first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
+ bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe);
}
iocbq = first_iocbq;
/*
@@ -10961,6 +10974,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
iocbq->iocb.ulpBdeCount++;
iocbq->iocb.unsli3.rcvsli3.bde2.tus.f.bdeSize =
LPFC_DATA_BUF_SIZE;
+ first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
+ bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe);
} else {
iocbq = lpfc_sli_get_iocbq(vport->phba);
if (!iocbq) {
@@ -10978,6 +10993,8 @@ lpfc_prep_seq(struct lpfc_vport *vport, struct hbq_dmabuf *seq_dmabuf)
iocbq->iocb.ulpBdeCount = 1;
iocbq->iocb.un.cont64[0].tus.f.bdeSize =
LPFC_DATA_BUF_SIZE;
+ first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
+ bf_get(lpfc_rcqe_length, &seq_dmabuf->rcqe);
iocbq->iocb.un.rcvels.remoteID = sid;
list_add_tail(&iocbq->list, &first_iocbq->list);
}
@@ -11324,7 +11341,7 @@ lpfc_sli4_init_vpi(struct lpfc_hba *phba, uint16_t vpi)
mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mboxq)
return -ENOMEM;
- lpfc_init_vpi(mboxq, vpi);
+ lpfc_init_vpi(phba, mboxq, vpi);
mbox_tmo = lpfc_mbox_tmo_val(phba, MBX_INIT_VPI);
rc = lpfc_sli_issue_mbox_wait(phba, mboxq, mbox_tmo);
if (rc != MBX_TIMEOUT)
@@ -11519,6 +11536,7 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
uint32_t alloc_len, req_len;
struct lpfc_mbx_read_fcf_tbl *read_fcf;
+ phba->fcoe_eventtag_at_fcf_scan = phba->fcoe_eventtag;
mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
if (!mboxq) {
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
@@ -11570,7 +11588,140 @@ lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
if (rc == MBX_NOT_FINISHED) {
lpfc_sli4_mbox_cmd_free(phba, mboxq);
error = -EIO;
- } else
+ } else {
+ spin_lock_irq(&phba->hbalock);
+ phba->hba_flag |= FCF_DISC_INPROGRESS;
+ spin_unlock_irq(&phba->hbalock);
error = 0;
+ }
return error;
}
+
+/**
+ * lpfc_sli_read_link_ste - Read region 23 to decide if link is disabled.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This function read region 23 and parse TLV for port status to
+ * decide if the user disaled the port. If the TLV indicates the
+ * port is disabled, the hba_flag is set accordingly.
+ **/
+void
+lpfc_sli_read_link_ste(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *pmb = NULL;
+ MAILBOX_t *mb;
+ uint8_t *rgn23_data = NULL;
+ uint32_t offset = 0, data_size, sub_tlv_len, tlv_offset;
+ int rc;
+
+ pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!pmb) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2600 lpfc_sli_read_serdes_param failed to"
+ " allocate mailbox memory\n");
+ goto out;
+ }
+ mb = &pmb->u.mb;
+
+ /* Get adapter Region 23 data */
+ rgn23_data = kzalloc(DMP_RGN23_SIZE, GFP_KERNEL);
+ if (!rgn23_data)
+ goto out;
+
+ do {
+ lpfc_dump_mem(phba, pmb, offset, DMP_REGION_23);
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL);
+
+ if (rc != MBX_SUCCESS) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ "2601 lpfc_sli_read_link_ste failed to"
+ " read config region 23 rc 0x%x Status 0x%x\n",
+ rc, mb->mbxStatus);
+ mb->un.varDmp.word_cnt = 0;
+ }
+ /*
+ * dump mem may return a zero when finished or we got a
+ * mailbox error, either way we are done.
+ */
+ if (mb->un.varDmp.word_cnt == 0)
+ break;
+ if (mb->un.varDmp.word_cnt > DMP_RGN23_SIZE - offset)
+ mb->un.varDmp.word_cnt = DMP_RGN23_SIZE - offset;
+
+ lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
+ rgn23_data + offset,
+ mb->un.varDmp.word_cnt);
+ offset += mb->un.varDmp.word_cnt;
+ } while (mb->un.varDmp.word_cnt && offset < DMP_RGN23_SIZE);
+
+ data_size = offset;
+ offset = 0;
+
+ if (!data_size)
+ goto out;
+
+ /* Check the region signature first */
+ if (memcmp(&rgn23_data[offset], LPFC_REGION23_SIGNATURE, 4)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2619 Config region 23 has bad signature\n");
+ goto out;
+ }
+ offset += 4;
+
+ /* Check the data structure version */
+ if (rgn23_data[offset] != LPFC_REGION23_VERSION) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2620 Config region 23 has bad version\n");
+ goto out;
+ }
+ offset += 4;
+
+ /* Parse TLV entries in the region */
+ while (offset < data_size) {
+ if (rgn23_data[offset] == LPFC_REGION23_LAST_REC)
+ break;
+ /*
+ * If the TLV is not driver specific TLV or driver id is
+ * not linux driver id, skip the record.
+ */
+ if ((rgn23_data[offset] != DRIVER_SPECIFIC_TYPE) ||
+ (rgn23_data[offset + 2] != LINUX_DRIVER_ID) ||
+ (rgn23_data[offset + 3] != 0)) {
+ offset += rgn23_data[offset + 1] * 4 + 4;
+ continue;
+ }
+
+ /* Driver found a driver specific TLV in the config region */
+ sub_tlv_len = rgn23_data[offset + 1] * 4;
+ offset += 4;
+ tlv_offset = 0;
+
+ /*
+ * Search for configured port state sub-TLV.
+ */
+ while ((offset < data_size) &&
+ (tlv_offset < sub_tlv_len)) {
+ if (rgn23_data[offset] == LPFC_REGION23_LAST_REC) {
+ offset += 4;
+ tlv_offset += 4;
+ break;
+ }
+ if (rgn23_data[offset] != PORT_STE_TYPE) {
+ offset += rgn23_data[offset + 1] * 4 + 4;
+ tlv_offset += rgn23_data[offset + 1] * 4 + 4;
+ continue;
+ }
+
+ /* This HBA contains PORT_STE configured */
+ if (!rgn23_data[offset + 2])
+ phba->hba_flag |= LINK_DISABLED;
+
+ goto out;
+ }
+ }
+out:
+ if (pmb)
+ mempool_free(pmb, phba->mbox_mem_pool);
+ kfree(rgn23_data);
+ return;
+}
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index 3b276b47d18..b5f4ba1a5c2 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -132,6 +132,7 @@ struct lpfc_sli4_link {
struct lpfc_fcf {
uint8_t fabric_name[8];
+ uint8_t switch_name[8];
uint8_t mac_addr[6];
uint16_t fcf_indx;
uint16_t fcfi;
@@ -150,6 +151,10 @@ struct lpfc_fcf {
#define LPFC_REGION23_SIGNATURE "RG23"
#define LPFC_REGION23_VERSION 1
#define LPFC_REGION23_LAST_REC 0xff
+#define DRIVER_SPECIFIC_TYPE 0xA2
+#define LINUX_DRIVER_ID 0x20
+#define PORT_STE_TYPE 0x1
+
struct lpfc_fip_param_hdr {
uint8_t type;
#define FCOE_PARAM_TYPE 0xA0
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index 41094e02304..9ae20af4bdb 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.3.3"
+#define LPFC_DRIVER_VERSION "8.3.4"
#define LPFC_DRIVER_NAME "lpfc"
#define LPFC_SP_DRIVER_HANDLER_NAME "lpfc:sp"
diff --git a/drivers/scsi/lpfc/lpfc_vport.c b/drivers/scsi/lpfc/lpfc_vport.c
index e0b49922193..606efa76754 100644
--- a/drivers/scsi/lpfc/lpfc_vport.c
+++ b/drivers/scsi/lpfc/lpfc_vport.c
@@ -313,22 +313,6 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
goto error_out;
}
- /*
- * In SLI4, the vpi must be activated before it can be used
- * by the port.
- */
- if (phba->sli_rev == LPFC_SLI_REV4) {
- rc = lpfc_sli4_init_vpi(phba, vpi);
- if (rc) {
- lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
- "1838 Failed to INIT_VPI on vpi %d "
- "status %d\n", vpi, rc);
- rc = VPORT_NORESOURCES;
- lpfc_free_vpi(phba, vpi);
- goto error_out;
- }
- }
-
/* Assign an unused board number */
if ((instance = lpfc_get_instance()) < 0) {
lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
@@ -367,12 +351,8 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
goto error_out;
}
- memcpy(vport->fc_portname.u.wwn, vport->fc_sparam.portName.u.wwn, 8);
- memcpy(vport->fc_nodename.u.wwn, vport->fc_sparam.nodeName.u.wwn, 8);
- if (fc_vport->node_name != 0)
- u64_to_wwn(fc_vport->node_name, vport->fc_nodename.u.wwn);
- if (fc_vport->port_name != 0)
- u64_to_wwn(fc_vport->port_name, vport->fc_portname.u.wwn);
+ u64_to_wwn(fc_vport->node_name, vport->fc_nodename.u.wwn);
+ u64_to_wwn(fc_vport->port_name, vport->fc_portname.u.wwn);
memcpy(&vport->fc_sparam.portName, vport->fc_portname.u.wwn, 8);
memcpy(&vport->fc_sparam.nodeName, vport->fc_nodename.u.wwn, 8);
@@ -404,7 +384,34 @@ lpfc_vport_create(struct fc_vport *fc_vport, bool disable)
*(struct lpfc_vport **)fc_vport->dd_data = vport;
vport->fc_vport = fc_vport;
+ /*
+ * In SLI4, the vpi must be activated before it can be used
+ * by the port.
+ */
+ if ((phba->sli_rev == LPFC_SLI_REV4) &&
+ (pport->vfi_state & LPFC_VFI_REGISTERED)) {
+ rc = lpfc_sli4_init_vpi(phba, vpi);
+ if (rc) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_VPORT,
+ "1838 Failed to INIT_VPI on vpi %d "
+ "status %d\n", vpi, rc);
+ rc = VPORT_NORESOURCES;
+ lpfc_free_vpi(phba, vpi);
+ goto error_out;
+ }
+ } else if (phba->sli_rev == LPFC_SLI_REV4) {
+ /*
+ * Driver cannot INIT_VPI now. Set the flags to
+ * init_vpi when reg_vfi complete.
+ */
+ vport->fc_flag |= FC_VPORT_NEEDS_INIT_VPI;
+ lpfc_vport_set_state(vport, FC_VPORT_LINKDOWN);
+ rc = VPORT_OK;
+ goto out;
+ }
+
if ((phba->link_state < LPFC_LINK_UP) ||
+ (pport->port_state < LPFC_FABRIC_CFG_LINK) ||
(phba->fc_topology == TOPOLOGY_LOOP)) {
lpfc_vport_set_state(vport, FC_VPORT_LINKDOWN);
rc = VPORT_OK;
@@ -661,7 +668,7 @@ lpfc_vport_delete(struct fc_vport *fc_vport)
lpfc_printf_log(vport->phba, KERN_WARNING,
LOG_VPORT,
"1829 CT command failed to "
- "delete objects on fabric. \n");
+ "delete objects on fabric\n");
}
/* First look for the Fabric ndlp */
ndlp = lpfc_findnode_did(vport, Fabric_DID);
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
index 35a13867495..d95d2f274cb 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -94,7 +94,7 @@ _base_fault_reset_work(struct work_struct *work)
int rc;
spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- if (ioc->ioc_reset_in_progress)
+ if (ioc->shost_recovery)
goto rearm_timer;
spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
@@ -687,6 +687,14 @@ _base_unmask_interrupts(struct MPT2SAS_ADAPTER *ioc)
ioc->mask_interrupts = 0;
}
+union reply_descriptor {
+ u64 word;
+ struct {
+ u32 low;
+ u32 high;
+ } u;
+};
+
/**
* _base_interrupt - MPT adapter (IOC) specific interrupt handler.
* @irq: irq number (not used)
@@ -698,47 +706,38 @@ _base_unmask_interrupts(struct MPT2SAS_ADAPTER *ioc)
static irqreturn_t
_base_interrupt(int irq, void *bus_id)
{
- union reply_descriptor {
- u64 word;
- struct {
- u32 low;
- u32 high;
- } u;
- };
union reply_descriptor rd;
- u32 post_index, post_index_next, completed_cmds;
+ u32 completed_cmds;
u8 request_desript_type;
u16 smid;
u8 cb_idx;
u32 reply;
u8 VF_ID;
- int i;
struct MPT2SAS_ADAPTER *ioc = bus_id;
+ Mpi2ReplyDescriptorsUnion_t *rpf;
if (ioc->mask_interrupts)
return IRQ_NONE;
- post_index = ioc->reply_post_host_index;
- request_desript_type = ioc->reply_post_free[post_index].
- Default.ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
+ rpf = &ioc->reply_post_free[ioc->reply_post_host_index];
+ request_desript_type = rpf->Default.ReplyFlags
+ & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
return IRQ_NONE;
completed_cmds = 0;
do {
- rd.word = ioc->reply_post_free[post_index].Words;
+ rd.word = rpf->Words;
if (rd.u.low == UINT_MAX || rd.u.high == UINT_MAX)
goto out;
reply = 0;
cb_idx = 0xFF;
- smid = le16_to_cpu(ioc->reply_post_free[post_index].
- Default.DescriptorTypeDependent1);
- VF_ID = ioc->reply_post_free[post_index].
- Default.VF_ID;
+ smid = le16_to_cpu(rpf->Default.DescriptorTypeDependent1);
+ VF_ID = rpf->Default.VF_ID;
if (request_desript_type ==
MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY) {
- reply = le32_to_cpu(ioc->reply_post_free[post_index].
- AddressReply.ReplyFrameAddress);
+ reply = le32_to_cpu
+ (rpf->AddressReply.ReplyFrameAddress);
} else if (request_desript_type ==
MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER)
goto next;
@@ -765,21 +764,27 @@ _base_interrupt(int irq, void *bus_id)
0 : ioc->reply_free_host_index + 1;
ioc->reply_free[ioc->reply_free_host_index] =
cpu_to_le32(reply);
+ wmb();
writel(ioc->reply_free_host_index,
&ioc->chip->ReplyFreeHostIndex);
- wmb();
}
next:
- post_index_next = (post_index == (ioc->reply_post_queue_depth -
- 1)) ? 0 : post_index + 1;
+
+ rpf->Words = ULLONG_MAX;
+ ioc->reply_post_host_index = (ioc->reply_post_host_index ==
+ (ioc->reply_post_queue_depth - 1)) ? 0 :
+ ioc->reply_post_host_index + 1;
request_desript_type =
- ioc->reply_post_free[post_index_next].Default.ReplyFlags
- & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
+ ioc->reply_post_free[ioc->reply_post_host_index].Default.
+ ReplyFlags & MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK;
completed_cmds++;
if (request_desript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED)
goto out;
- post_index = post_index_next;
+ if (!ioc->reply_post_host_index)
+ rpf = ioc->reply_post_free;
+ else
+ rpf++;
} while (1);
out:
@@ -787,19 +792,8 @@ _base_interrupt(int irq, void *bus_id)
if (!completed_cmds)
return IRQ_NONE;
- /* reply post descriptor handling */
- post_index_next = ioc->reply_post_host_index;
- for (i = 0 ; i < completed_cmds; i++) {
- post_index = post_index_next;
- /* poison the reply post descriptor */
- ioc->reply_post_free[post_index_next].Words = ULLONG_MAX;
- post_index_next = (post_index ==
- (ioc->reply_post_queue_depth - 1))
- ? 0 : post_index + 1;
- }
- ioc->reply_post_host_index = post_index_next;
- writel(post_index_next, &ioc->chip->ReplyPostHostIndex);
wmb();
+ writel(ioc->reply_post_host_index, &ioc->chip->ReplyPostHostIndex);
return IRQ_HANDLED;
}
@@ -1542,6 +1536,8 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
(ioc->bios_pg3.BiosVersion & 0x0000FF00) >> 8,
ioc->bios_pg3.BiosVersion & 0x000000FF);
+ _base_display_dell_branding(ioc);
+
printk(MPT2SAS_INFO_FMT "Protocol=(", ioc->name);
if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR) {
@@ -1554,8 +1550,6 @@ _base_display_ioc_capabilities(struct MPT2SAS_ADAPTER *ioc)
i++;
}
- _base_display_dell_branding(ioc);
-
i = 0;
printk("), ");
printk("Capabilities=(");
@@ -1627,6 +1621,9 @@ _base_static_config_pages(struct MPT2SAS_ADAPTER *ioc)
u32 iounit_pg1_flags;
mpt2sas_config_get_manufacturing_pg0(ioc, &mpi_reply, &ioc->manu_pg0);
+ if (ioc->ir_firmware)
+ mpt2sas_config_get_manufacturing_pg10(ioc, &mpi_reply,
+ &ioc->manu_pg10);
mpt2sas_config_get_bios_pg2(ioc, &mpi_reply, &ioc->bios_pg2);
mpt2sas_config_get_bios_pg3(ioc, &mpi_reply, &ioc->bios_pg3);
mpt2sas_config_get_ioc_pg8(ioc, &mpi_reply, &ioc->ioc_pg8);
@@ -1647,7 +1644,7 @@ _base_static_config_pages(struct MPT2SAS_ADAPTER *ioc)
iounit_pg1_flags |=
MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING;
ioc->iounit_pg1.Flags = cpu_to_le32(iounit_pg1_flags);
- mpt2sas_config_set_iounit_pg1(ioc, &mpi_reply, ioc->iounit_pg1);
+ mpt2sas_config_set_iounit_pg1(ioc, &mpi_reply, &ioc->iounit_pg1);
}
/**
@@ -3303,13 +3300,11 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc)
ioc->tm_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
ioc->tm_cmds.status = MPT2_CMD_NOT_USED;
mutex_init(&ioc->tm_cmds.mutex);
- init_completion(&ioc->tm_cmds.done);
/* config page internal command bits */
ioc->config_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
ioc->config_cmds.status = MPT2_CMD_NOT_USED;
mutex_init(&ioc->config_cmds.mutex);
- init_completion(&ioc->config_cmds.done);
/* ctl module internal command bits */
ioc->ctl_cmds.reply = kzalloc(ioc->reply_sz, GFP_KERNEL);
@@ -3433,6 +3428,7 @@ _base_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
if (ioc->config_cmds.status & MPT2_CMD_PENDING) {
ioc->config_cmds.status |= MPT2_CMD_RESET;
mpt2sas_base_free_smid(ioc, ioc->config_cmds.smid);
+ ioc->config_cmds.smid = USHORT_MAX;
complete(&ioc->config_cmds.done);
}
break;
@@ -3501,20 +3497,13 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
__func__));
spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- if (ioc->ioc_reset_in_progress) {
+ if (ioc->shost_recovery) {
spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
printk(MPT2SAS_ERR_FMT "%s: busy\n",
ioc->name, __func__);
return -EBUSY;
}
- ioc->ioc_reset_in_progress = 1;
ioc->shost_recovery = 1;
- if (ioc->shost->shost_state == SHOST_RUNNING) {
- /* set back to SHOST_RUNNING in mpt2sas_scsih.c */
- scsi_host_set_state(ioc->shost, SHOST_RECOVERY);
- printk(MPT2SAS_INFO_FMT "putting controller into "
- "SHOST_RECOVERY\n", ioc->name);
- }
spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
_base_reset_handler(ioc, MPT2_IOC_PRE_RESET);
@@ -3534,7 +3523,10 @@ mpt2sas_base_hard_reset_handler(struct MPT2SAS_ADAPTER *ioc, int sleep_flag,
ioc->name, __func__, ((r == 0) ? "SUCCESS" : "FAILED")));
spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- ioc->ioc_reset_in_progress = 0;
+ ioc->shost_recovery = 0;
spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+
+ if (!r)
+ _base_reset_handler(ioc, MPT2_IOC_RUNNING);
return r;
}
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h
index acdcff150a3..2faab1e690e 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.h
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.h
@@ -69,10 +69,10 @@
#define MPT2SAS_DRIVER_NAME "mpt2sas"
#define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>"
#define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver"
-#define MPT2SAS_DRIVER_VERSION "01.100.04.00"
+#define MPT2SAS_DRIVER_VERSION "01.100.06.00"
#define MPT2SAS_MAJOR_VERSION 01
#define MPT2SAS_MINOR_VERSION 100
-#define MPT2SAS_BUILD_VERSION 04
+#define MPT2SAS_BUILD_VERSION 06
#define MPT2SAS_RELEASE_VERSION 00
/*
@@ -119,6 +119,7 @@
#define MPT2_IOC_PRE_RESET 1 /* prior to host reset */
#define MPT2_IOC_AFTER_RESET 2 /* just after host reset */
#define MPT2_IOC_DONE_RESET 3 /* links re-initialized */
+#define MPT2_IOC_RUNNING 4 /* shost running */
/*
* logging format
@@ -196,6 +197,38 @@ struct MPT2SAS_TARGET {
* @block: device is in SDEV_BLOCK state
* @tlr_snoop_check: flag used in determining whether to disable TLR
*/
+
+/* OEM Identifiers */
+#define MFG10_OEM_ID_INVALID (0x00000000)
+#define MFG10_OEM_ID_DELL (0x00000001)
+#define MFG10_OEM_ID_FSC (0x00000002)
+#define MFG10_OEM_ID_SUN (0x00000003)
+#define MFG10_OEM_ID_IBM (0x00000004)
+
+/* GENERIC Flags 0*/
+#define MFG10_GF0_OCE_DISABLED (0x00000001)
+#define MFG10_GF0_R1E_DRIVE_COUNT (0x00000002)
+#define MFG10_GF0_R10_DISPLAY (0x00000004)
+#define MFG10_GF0_SSD_DATA_SCRUB_DISABLE (0x00000008)
+#define MFG10_GF0_SINGLE_DRIVE_R0 (0x00000010)
+
+/* OEM Specific Flags will come from OEM specific header files */
+typedef struct _MPI2_CONFIG_PAGE_MAN_10 {
+ MPI2_CONFIG_PAGE_HEADER Header; /* 00h */
+ U8 OEMIdentifier; /* 04h */
+ U8 Reserved1; /* 05h */
+ U16 Reserved2; /* 08h */
+ U32 Reserved3; /* 0Ch */
+ U32 GenericFlags0; /* 10h */
+ U32 GenericFlags1; /* 14h */
+ U32 Reserved4; /* 18h */
+ U32 OEMSpecificFlags0; /* 1Ch */
+ U32 OEMSpecificFlags1; /* 20h */
+ U32 Reserved5[18]; /* 24h-60h*/
+} MPI2_CONFIG_PAGE_MAN_10,
+ MPI2_POINTER PTR_MPI2_CONFIG_PAGE_MAN_10,
+ Mpi2ManufacturingPage10_t, MPI2_POINTER pMpi2ManufacturingPage10_t;
+
struct MPT2SAS_DEVICE {
struct MPT2SAS_TARGET *sas_target;
unsigned int lun;
@@ -431,7 +464,7 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
* @fw_event_list: list of fw events
* @aen_event_read_flag: event log was read
* @broadcast_aen_busy: broadcast aen waiting to be serviced
- * @ioc_reset_in_progress: host reset in progress
+ * @shost_recovery: host reset in progress
* @ioc_reset_in_progress_lock:
* @ioc_link_reset_in_progress: phy/hard reset in progress
* @ignore_loginfos: ignore loginfos during task managment
@@ -460,6 +493,7 @@ typedef void (*MPT_ADD_SGE)(void *paddr, u32 flags_length, dma_addr_t dma_addr);
* @facts: static facts data
* @pfacts: static port facts data
* @manu_pg0: static manufacturing page 0
+ * @manu_pg10: static manufacturing page 10
* @bios_pg2: static bios page 2
* @bios_pg3: static bios page 3
* @ioc_pg8: static ioc page 8
@@ -544,7 +578,6 @@ struct MPT2SAS_ADAPTER {
/* misc flags */
int aen_event_read_flag;
u8 broadcast_aen_busy;
- u8 ioc_reset_in_progress;
u8 shost_recovery;
spinlock_t ioc_reset_in_progress_lock;
u8 ioc_link_reset_in_progress;
@@ -663,6 +696,7 @@ struct MPT2SAS_ADAPTER {
dma_addr_t diag_buffer_dma[MPI2_DIAG_BUF_TYPE_COUNT];
u8 diag_buffer_status[MPI2_DIAG_BUF_TYPE_COUNT];
u32 unique_id[MPI2_DIAG_BUF_TYPE_COUNT];
+ Mpi2ManufacturingPage10_t manu_pg10;
u32 product_specific[MPI2_DIAG_BUF_TYPE_COUNT][23];
u32 diagnostic_flags[MPI2_DIAG_BUF_TYPE_COUNT];
};
@@ -734,6 +768,8 @@ void mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 re
int mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys);
int mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc,
Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page);
+int mpt2sas_config_get_manufacturing_pg10(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage10_t *config_page);
int mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
*mpi_reply, Mpi2BiosPage2_t *config_page);
int mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
@@ -749,7 +785,7 @@ int mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRep
int mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
*mpi_reply, Mpi2IOUnitPage1_t *config_page);
int mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
- *mpi_reply, Mpi2IOUnitPage1_t config_page);
+ *mpi_reply, Mpi2IOUnitPage1_t *config_page);
int mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
*mpi_reply, Mpi2SasIOUnitPage1_t *config_page, u16 sz);
int mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
@@ -776,7 +812,6 @@ int mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle,
u16 *volume_handle);
int mpt2sas_config_get_volume_wwid(struct MPT2SAS_ADAPTER *ioc, u16 volume_handle,
u64 *wwid);
-
/* ctl shared API */
extern struct device_attribute *mpt2sas_host_attrs[];
extern struct device_attribute *mpt2sas_dev_attrs[];
@@ -798,9 +833,11 @@ int mpt2sas_transport_add_host_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
*mpt2sas_phy, Mpi2SasPhyPage0_t phy_pg0, struct device *parent_dev);
int mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
*mpt2sas_phy, Mpi2ExpanderPage1_t expander_pg1, struct device *parent_dev);
-void mpt2sas_transport_update_phy_link_change(struct MPT2SAS_ADAPTER *ioc, u16 handle,
+void mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc, u16 handle,
u16 attached_handle, u8 phy_number, u8 link_rate);
extern struct sas_function_template mpt2sas_transport_functions;
extern struct scsi_transport_template *mpt2sas_transport_template;
+extern int scsi_internal_device_block(struct scsi_device *sdev);
+extern int scsi_internal_device_unblock(struct scsi_device *sdev);
#endif /* MPT2SAS_BASE_H_INCLUDED */
diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c
index 6ddee161beb..ab8c560865d 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_config.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_config.c
@@ -72,15 +72,15 @@
/**
* struct config_request - obtain dma memory via routine
- * @config_page_sz: size
- * @config_page: virt pointer
- * @config_page_dma: phys pointer
+ * @sz: size
+ * @page: virt pointer
+ * @page_dma: phys pointer
*
*/
struct config_request{
- u16 config_page_sz;
- void *config_page;
- dma_addr_t config_page_dma;
+ u16 sz;
+ void *page;
+ dma_addr_t page_dma;
};
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
@@ -175,6 +175,55 @@ _config_display_some_debug(struct MPT2SAS_ADAPTER *ioc, u16 smid,
#endif
/**
+ * _config_alloc_config_dma_memory - obtain physical memory
+ * @ioc: per adapter object
+ * @mem: struct config_request
+ *
+ * A wrapper for obtaining dma-able memory for config page request.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static int
+_config_alloc_config_dma_memory(struct MPT2SAS_ADAPTER *ioc,
+ struct config_request *mem)
+{
+ int r = 0;
+
+ if (mem->sz > ioc->config_page_sz) {
+ mem->page = dma_alloc_coherent(&ioc->pdev->dev, mem->sz,
+ &mem->page_dma, GFP_KERNEL);
+ if (!mem->page) {
+ printk(MPT2SAS_ERR_FMT "%s: dma_alloc_coherent"
+ " failed asking for (%d) bytes!!\n",
+ ioc->name, __func__, mem->sz);
+ r = -ENOMEM;
+ }
+ } else { /* use tmp buffer if less than 512 bytes */
+ mem->page = ioc->config_page;
+ mem->page_dma = ioc->config_page_dma;
+ }
+ return r;
+}
+
+/**
+ * _config_free_config_dma_memory - wrapper to free the memory
+ * @ioc: per adapter object
+ * @mem: struct config_request
+ *
+ * A wrapper to free dma-able memory when using _config_alloc_config_dma_memory.
+ *
+ * Returns 0 for success, non-zero for failure.
+ */
+static void
+_config_free_config_dma_memory(struct MPT2SAS_ADAPTER *ioc,
+ struct config_request *mem)
+{
+ if (mem->sz > ioc->config_page_sz)
+ dma_free_coherent(&ioc->pdev->dev, mem->sz, mem->page,
+ mem->page_dma);
+}
+
+/**
* mpt2sas_config_done - config page completion routine
* @ioc: per adapter object
* @smid: system request message index
@@ -206,6 +255,7 @@ mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
_config_display_some_debug(ioc, smid, "config_done", mpi_reply);
#endif
+ ioc->config_cmds.smid = USHORT_MAX;
complete(&ioc->config_cmds.done);
}
@@ -215,7 +265,9 @@ mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
* @mpi_request: request message frame
* @mpi_reply: reply mf payload returned from firmware
* @timeout: timeout in seconds
- * Context: sleep, the calling function needs to acquire the config_cmds.mutex
+ * @config_page: contents of the config page
+ * @config_page_sz: size of config page
+ * Context: sleep
*
* A generic API for config page requests to firmware.
*
@@ -228,16 +280,17 @@ mpt2sas_config_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
*/
static int
_config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
- *mpi_request, Mpi2ConfigReply_t *mpi_reply, int timeout)
+ *mpi_request, Mpi2ConfigReply_t *mpi_reply, int timeout,
+ void *config_page, u16 config_page_sz)
{
u16 smid;
u32 ioc_state;
unsigned long timeleft;
Mpi2ConfigRequest_t *config_request;
int r;
- u8 retry_count;
- u8 issue_host_reset = 0;
+ u8 retry_count, issue_host_reset = 0;
u16 wait_state_count;
+ struct config_request mem;
mutex_lock(&ioc->config_cmds.mutex);
if (ioc->config_cmds.status != MPT2_CMD_NOT_USED) {
@@ -246,12 +299,44 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
mutex_unlock(&ioc->config_cmds.mutex);
return -EAGAIN;
}
+
retry_count = 0;
+ memset(&mem, 0, sizeof(struct config_request));
+
+ if (config_page) {
+ mpi_request->Header.PageVersion = mpi_reply->Header.PageVersion;
+ mpi_request->Header.PageNumber = mpi_reply->Header.PageNumber;
+ mpi_request->Header.PageType = mpi_reply->Header.PageType;
+ mpi_request->Header.PageLength = mpi_reply->Header.PageLength;
+ mpi_request->ExtPageLength = mpi_reply->ExtPageLength;
+ mpi_request->ExtPageType = mpi_reply->ExtPageType;
+ if (mpi_request->Header.PageLength)
+ mem.sz = mpi_request->Header.PageLength * 4;
+ else
+ mem.sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
+ r = _config_alloc_config_dma_memory(ioc, &mem);
+ if (r != 0)
+ goto out;
+ if (mpi_request->Action ==
+ MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT) {
+ ioc->base_add_sg_single(&mpi_request->PageBufferSGE,
+ MPT2_CONFIG_COMMON_WRITE_SGLFLAGS | mem.sz,
+ mem.page_dma);
+ memcpy(mem.page, config_page, min_t(u16, mem.sz,
+ config_page_sz));
+ } else {
+ memset(config_page, 0, config_page_sz);
+ ioc->base_add_sg_single(&mpi_request->PageBufferSGE,
+ MPT2_CONFIG_COMMON_SGLFLAGS | mem.sz, mem.page_dma);
+ }
+ }
retry_config:
if (retry_count) {
- if (retry_count > 2) /* attempt only 2 retries */
- return -EFAULT;
+ if (retry_count > 2) { /* attempt only 2 retries */
+ r = -EFAULT;
+ goto free_mem;
+ }
printk(MPT2SAS_INFO_FMT "%s: attempting retry (%d)\n",
ioc->name, __func__, retry_count);
}
@@ -262,8 +347,9 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
printk(MPT2SAS_ERR_FMT
"%s: failed due to ioc not operational\n",
ioc->name, __func__);
+ ioc->config_cmds.status = MPT2_CMD_NOT_USED;
r = -EFAULT;
- goto out;
+ goto free_mem;
}
ssleep(1);
ioc_state = mpt2sas_base_get_iocstate(ioc, 1);
@@ -279,8 +365,9 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
if (!smid) {
printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",
ioc->name, __func__);
+ ioc->config_cmds.status = MPT2_CMD_NOT_USED;
r = -EAGAIN;
- goto out;
+ goto free_mem;
}
r = 0;
@@ -292,6 +379,7 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
#ifdef CONFIG_SCSI_MPT2SAS_LOGGING
_config_display_some_debug(ioc, smid, "config_request", NULL);
#endif
+ init_completion(&ioc->config_cmds.done);
mpt2sas_base_put_smid_default(ioc, smid, config_request->VF_ID);
timeleft = wait_for_completion_timeout(&ioc->config_cmds.done,
timeout*HZ);
@@ -303,22 +391,31 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t
retry_count++;
if (ioc->config_cmds.smid == smid)
mpt2sas_base_free_smid(ioc, smid);
- if ((ioc->shost_recovery) ||
- (ioc->config_cmds.status & MPT2_CMD_RESET))
+ if ((ioc->shost_recovery) || (ioc->config_cmds.status &
+ MPT2_CMD_RESET))
goto retry_config;
issue_host_reset = 1;
r = -EFAULT;
- goto out;
+ goto free_mem;
}
+
if (ioc->config_cmds.status & MPT2_CMD_REPLY_VALID)
memcpy(mpi_reply, ioc->config_cmds.reply,
sizeof(Mpi2ConfigReply_t));
if (retry_count)
- printk(MPT2SAS_INFO_FMT "%s: retry completed!!\n",
- ioc->name, __func__);
-out:
+ printk(MPT2SAS_INFO_FMT "%s: retry (%d) completed!!\n",
+ ioc->name, __func__, retry_count);
+ if (config_page && mpi_request->Action ==
+ MPI2_CONFIG_ACTION_PAGE_READ_CURRENT)
+ memcpy(config_page, mem.page, min_t(u16, mem.sz,
+ config_page_sz));
+ free_mem:
+ if (config_page)
+ _config_free_config_dma_memory(ioc, &mem);
+ out:
ioc->config_cmds.status = MPT2_CMD_NOT_USED;
mutex_unlock(&ioc->config_cmds.mutex);
+
if (issue_host_reset)
mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP,
FORCE_BIG_HAMMER);
@@ -326,46 +423,43 @@ out:
}
/**
- * _config_alloc_config_dma_memory - obtain physical memory
+ * mpt2sas_config_get_manufacturing_pg0 - obtain manufacturing page 0
* @ioc: per adapter object
- * @mem: struct config_request
- *
- * A wrapper for obtaining dma-able memory for config page request.
+ * @mpi_reply: reply mf payload returned from firmware
+ * @config_page: contents of the config page
+ * Context: sleep.
*
* Returns 0 for success, non-zero for failure.
*/
-static int
-_config_alloc_config_dma_memory(struct MPT2SAS_ADAPTER *ioc,
- struct config_request *mem)
+int
+mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page)
{
- int r = 0;
+ Mpi2ConfigRequest_t mpi_request;
+ int r;
- mem->config_page = pci_alloc_consistent(ioc->pdev, mem->config_page_sz,
- &mem->config_page_dma);
- if (!mem->config_page)
- r = -ENOMEM;
- return r;
-}
+ memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
+ mpi_request.Function = MPI2_FUNCTION_CONFIG;
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
+ mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
+ mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
+ mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
+ if (r)
+ goto out;
-/**
- * _config_free_config_dma_memory - wrapper to free the memory
- * @ioc: per adapter object
- * @mem: struct config_request
- *
- * A wrapper to free dma-able memory when using _config_alloc_config_dma_memory.
- *
- * Returns 0 for success, non-zero for failure.
- */
-static void
-_config_free_config_dma_memory(struct MPT2SAS_ADAPTER *ioc,
- struct config_request *mem)
-{
- pci_free_consistent(ioc->pdev, mem->config_page_sz, mem->config_page,
- mem->config_page_dma);
+ mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
+ r = _config_request(ioc, &mpi_request, mpi_reply,
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
+ out:
+ return r;
}
/**
- * mpt2sas_config_get_manufacturing_pg0 - obtain manufacturing page 0
+ * mpt2sas_config_get_manufacturing_pg10 - obtain manufacturing page 10
* @ioc: per adapter object
* @mpi_reply: reply mf payload returned from firmware
* @config_page: contents of the config page
@@ -374,53 +468,28 @@ _config_free_config_dma_memory(struct MPT2SAS_ADAPTER *ioc,
* Returns 0 for success, non-zero for failure.
*/
int
-mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage0_t *config_page)
+mpt2sas_config_get_manufacturing_pg10(struct MPT2SAS_ADAPTER *ioc,
+ Mpi2ConfigReply_t *mpi_reply, Mpi2ManufacturingPage10_t *config_page)
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2ManufacturingPage0_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_MANUFACTURING;
- mpi_request.Header.PageNumber = 0;
+ mpi_request.Header.PageNumber = 10;
mpi_request.Header.PageVersion = MPI2_MANUFACTURING0_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
- mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2ManufacturingPage0_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -440,9 +509,7 @@ mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc,
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2BiosPage2_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -451,37 +518,14 @@ mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc,
mpi_request.Header.PageVersion = MPI2_BIOSPAGE2_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
- mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2BiosPage2_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -501,9 +545,7 @@ mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2BiosPage3_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -512,37 +554,14 @@ mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
mpi_request.Header.PageVersion = MPI2_BIOSPAGE3_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
- mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2BiosPage3_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -562,9 +581,7 @@ mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc,
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2IOUnitPage0_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -573,37 +590,14 @@ mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc,
mpi_request.Header.PageVersion = MPI2_IOUNITPAGE0_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
- mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2IOUnitPage0_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -623,9 +617,7 @@ mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2IOUnitPage1_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -634,37 +626,14 @@ mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
- mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2IOUnitPage1_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -680,11 +649,10 @@ mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
*/
int
mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
- Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t config_page)
+ Mpi2ConfigReply_t *mpi_reply, Mpi2IOUnitPage1_t *config_page)
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
@@ -694,38 +662,14 @@ mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc,
mpi_request.Header.PageVersion = MPI2_IOUNITPAGE1_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_WRITE_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
- mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
-
- memset(mem.config_page, 0, mem.config_page_sz);
- memcpy(mem.config_page, &config_page,
- sizeof(Mpi2IOUnitPage1_t));
-
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_WRITE_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -745,9 +689,7 @@ mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc,
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2IOCPage8_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -756,37 +698,14 @@ mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc,
mpi_request.Header.PageVersion = MPI2_IOCPAGE8_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
- mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2IOCPage8_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -808,9 +727,7 @@ mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2SasDevicePage0_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -820,39 +737,15 @@ mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
mpi_request.Header.PageNumber = 0;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.PageAddress = cpu_to_le32(form | handle);
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
- mpi_request.ExtPageType = mpi_reply->ExtPageType;
- mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2SasDevicePage0_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -874,9 +767,7 @@ mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2SasDevicePage1_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -886,39 +777,15 @@ mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
mpi_request.Header.PageNumber = 1;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.PageAddress = cpu_to_le32(form | handle);
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
- mpi_request.ExtPageType = mpi_reply->ExtPageType;
- mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2SasDevicePage1_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -936,11 +803,11 @@ mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys)
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
u16 ioc_status;
Mpi2ConfigReply_t mpi_reply;
Mpi2SasIOUnitPage0_t config_page;
+ *num_phys = 0;
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -950,44 +817,20 @@ mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys)
mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, &mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply.Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply.Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply.Header.PageType;
- mpi_request.ExtPageLength = mpi_reply.ExtPageLength;
- mpi_request.ExtPageType = mpi_reply.ExtPageType;
- mem.config_page_sz = le16_to_cpu(mpi_reply.ExtPageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, &mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page,
+ sizeof(Mpi2SasIOUnitPage0_t));
if (!r) {
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
- memcpy(&config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2SasIOUnitPage0_t)));
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS)
*num_phys = config_page.NumPhys;
- }
}
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
out:
return r;
}
@@ -1011,8 +854,7 @@ mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sz);
+
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -1022,37 +864,13 @@ mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
- mpi_request.ExtPageType = mpi_reply->ExtPageType;
- mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, sz, mem.config_page_sz));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
out:
return r;
}
@@ -1076,9 +894,7 @@ mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sz);
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -1088,37 +904,13 @@ mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
mpi_request.Header.PageVersion = MPI2_SASIOUNITPAGE0_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
- mpi_request.ExtPageType = mpi_reply->ExtPageType;
- mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, sz, mem.config_page_sz));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
out:
return r;
}
@@ -1140,9 +932,7 @@ mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2ExpanderPage0_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -1152,39 +942,15 @@ mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
mpi_request.Header.PageVersion = MPI2_SASEXPANDER0_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.PageAddress = cpu_to_le32(form | handle);
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
- mpi_request.ExtPageType = mpi_reply->ExtPageType;
- mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2ExpanderPage0_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -1207,9 +973,7 @@ mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2ExpanderPage1_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -1219,7 +983,7 @@ mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
mpi_request.Header.PageVersion = MPI2_SASEXPANDER1_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
@@ -1227,33 +991,9 @@ mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
cpu_to_le32(MPI2_SAS_EXPAND_PGAD_FORM_HNDL_PHY_NUM |
(phy_number << MPI2_SAS_EXPAND_PGAD_PHYNUM_SHIFT) | handle);
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
- mpi_request.ExtPageType = mpi_reply->ExtPageType;
- mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2ExpanderPage1_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -1275,9 +1015,7 @@ mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2SasEnclosurePage0_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -1287,39 +1025,15 @@ mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
mpi_request.Header.PageVersion = MPI2_SASENCLOSURE0_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.PageAddress = cpu_to_le32(form | handle);
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
- mpi_request.ExtPageType = mpi_reply->ExtPageType;
- mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2SasEnclosurePage0_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -1340,9 +1054,7 @@ mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2SasPhyPage0_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -1352,40 +1064,16 @@ mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
mpi_request.Header.PageVersion = MPI2_SASPHY0_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.PageAddress =
cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number);
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
- mpi_request.ExtPageType = mpi_reply->ExtPageType;
- mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2SasPhyPage0_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -1406,9 +1094,7 @@ mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2SasPhyPage1_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -1418,40 +1104,16 @@ mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
mpi_request.Header.PageVersion = MPI2_SASPHY1_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.PageAddress =
cpu_to_le32(MPI2_SAS_PHY_PGAD_FORM_PHY_NUMBER | phy_number);
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.ExtPageLength = mpi_reply->ExtPageLength;
- mpi_request.ExtPageType = mpi_reply->ExtPageType;
- mem.config_page_sz = le16_to_cpu(mpi_reply->ExtPageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2SasPhyPage1_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -1474,9 +1136,7 @@ mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc,
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
- memset(config_page, 0, sizeof(Mpi2RaidVolPage1_t));
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
@@ -1485,38 +1145,15 @@ mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc,
mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE1_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.PageAddress = cpu_to_le32(form | handle);
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
- mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2RaidVolPage1_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -1535,10 +1172,9 @@ mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle,
u8 *num_pds)
{
Mpi2ConfigRequest_t mpi_request;
- Mpi2RaidVolPage0_t *config_page;
+ Mpi2RaidVolPage0_t config_page;
Mpi2ConfigReply_t mpi_reply;
int r;
- struct config_request mem;
u16 ioc_status;
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
@@ -1550,43 +1186,23 @@ mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle,
mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, &mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.PageAddress =
cpu_to_le32(MPI2_RAID_VOLUME_PGAD_FORM_HANDLE | handle);
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply.Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply.Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply.Header.PageType;
- mpi_request.Header.PageLength = mpi_reply.Header.PageLength;
- mem.config_page_sz = le16_to_cpu(mpi_reply.Header.PageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, &mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, &config_page,
+ sizeof(Mpi2RaidVolPage0_t));
if (!r) {
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
MPI2_IOCSTATUS_MASK;
- if (ioc_status == MPI2_IOCSTATUS_SUCCESS) {
- config_page = mem.config_page;
- *num_pds = config_page->NumPhysDisks;
- }
+ if (ioc_status == MPI2_IOCSTATUS_SUCCESS)
+ *num_pds = config_page.NumPhysDisks;
}
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
out:
return r;
}
@@ -1610,10 +1226,8 @@ mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc,
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- memset(config_page, 0, sz);
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_VOLUME;
@@ -1621,37 +1235,14 @@ mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc,
mpi_request.Header.PageVersion = MPI2_RAIDVOLPAGE0_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.PageAddress = cpu_to_le32(form | handle);
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
- mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, sz, mem.config_page_sz));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page, sz);
out:
return r;
}
@@ -1674,10 +1265,8 @@ mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
{
Mpi2ConfigRequest_t mpi_request;
int r;
- struct config_request mem;
memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t));
- memset(config_page, 0, sizeof(Mpi2RaidPhysDiskPage0_t));
mpi_request.Function = MPI2_FUNCTION_CONFIG;
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER;
mpi_request.Header.PageType = MPI2_CONFIG_PAGETYPE_RAID_PHYSDISK;
@@ -1685,38 +1274,15 @@ mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t
mpi_request.Header.PageVersion = MPI2_RAIDPHYSDISKPAGE0_PAGEVERSION;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.PageAddress = cpu_to_le32(form | form_specific);
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply->Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply->Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply->Header.PageType;
- mpi_request.Header.PageLength = mpi_reply->Header.PageLength;
- mem.config_page_sz = le16_to_cpu(mpi_reply->Header.PageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
r = _config_request(ioc, &mpi_request, mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
- if (!r)
- memcpy(config_page, mem.config_page,
- min_t(u16, mem.config_page_sz,
- sizeof(Mpi2RaidPhysDiskPage0_t)));
-
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ sizeof(*config_page));
out:
return r;
}
@@ -1734,11 +1300,10 @@ int
mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle,
u16 *volume_handle)
{
- Mpi2RaidConfigurationPage0_t *config_page;
+ Mpi2RaidConfigurationPage0_t *config_page = NULL;
Mpi2ConfigRequest_t mpi_request;
Mpi2ConfigReply_t mpi_reply;
- int r, i;
- struct config_request mem;
+ int r, i, config_page_sz;
u16 ioc_status;
*volume_handle = 0;
@@ -1751,40 +1316,27 @@ mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle,
mpi_request.Header.PageNumber = 0;
mpt2sas_base_build_zero_len_sge(ioc, &mpi_request.PageBufferSGE);
r = _config_request(ioc, &mpi_request, &mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, NULL, 0);
if (r)
goto out;
mpi_request.PageAddress =
cpu_to_le32(MPI2_RAID_PGAD_FORM_ACTIVE_CONFIG);
mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_READ_CURRENT;
- mpi_request.Header.PageVersion = mpi_reply.Header.PageVersion;
- mpi_request.Header.PageNumber = mpi_reply.Header.PageNumber;
- mpi_request.Header.PageType = mpi_reply.Header.PageType;
- mpi_request.ExtPageLength = mpi_reply.ExtPageLength;
- mpi_request.ExtPageType = mpi_reply.ExtPageType;
- mem.config_page_sz = le16_to_cpu(mpi_reply.ExtPageLength) * 4;
- if (mem.config_page_sz > ioc->config_page_sz) {
- r = _config_alloc_config_dma_memory(ioc, &mem);
- if (r)
- goto out;
- } else {
- mem.config_page_dma = ioc->config_page_dma;
- mem.config_page = ioc->config_page;
- }
- ioc->base_add_sg_single(&mpi_request.PageBufferSGE,
- MPT2_CONFIG_COMMON_SGLFLAGS | mem.config_page_sz,
- mem.config_page_dma);
+ config_page_sz = (le16_to_cpu(mpi_reply.ExtPageLength) * 4);
+ config_page = kmalloc(config_page_sz, GFP_KERNEL);
+ if (!config_page)
+ goto out;
r = _config_request(ioc, &mpi_request, &mpi_reply,
- MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT);
+ MPT2_CONFIG_PAGE_DEFAULT_TIMEOUT, config_page,
+ config_page_sz);
if (r)
goto out;
r = -1;
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
if (ioc_status != MPI2_IOCSTATUS_SUCCESS)
- goto done;
- config_page = mem.config_page;
+ goto out;
for (i = 0; i < config_page->NumElements; i++) {
if ((config_page->ConfigElement[i].ElementFlags &
MPI2_RAIDCONFIG0_EFLAGS_MASK_ELEMENT_TYPE) !=
@@ -1795,15 +1347,11 @@ mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle,
*volume_handle = le16_to_cpu(config_page->
ConfigElement[i].VolDevHandle);
r = 0;
- goto done;
+ goto out;
}
}
-
- done:
- if (mem.config_page_sz > ioc->config_page_sz)
- _config_free_config_dma_memory(ioc, &mem);
-
out:
+ kfree(config_page);
return r;
}
diff --git a/drivers/scsi/mpt2sas/mpt2sas_ctl.c b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
index 14e473d1fa7..c2a51018910 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_ctl.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_ctl.c
@@ -1963,7 +1963,6 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg)
{
enum block_state state;
long ret = -EINVAL;
- unsigned long flags;
state = (file->f_flags & O_NONBLOCK) ? NON_BLOCKING :
BLOCKING;
@@ -1989,13 +1988,8 @@ _ctl_ioctl_main(struct file *file, unsigned int cmd, void __user *arg)
!ioc)
return -ENODEV;
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- if (ioc->shost_recovery) {
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock,
- flags);
+ if (ioc->shost_recovery)
return -EAGAIN;
- }
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
if (_IOC_SIZE(cmd) == sizeof(struct mpt2_ioctl_command)) {
uarg = arg;
@@ -2098,7 +2092,6 @@ _ctl_compat_mpt_command(struct file *file, unsigned cmd, unsigned long arg)
struct mpt2_ioctl_command karg;
struct MPT2SAS_ADAPTER *ioc;
enum block_state state;
- unsigned long flags;
if (_IOC_SIZE(cmd) != sizeof(struct mpt2_ioctl_command32))
return -EINVAL;
@@ -2113,13 +2106,8 @@ _ctl_compat_mpt_command(struct file *file, unsigned cmd, unsigned long arg)
if (_ctl_verify_adapter(karg32.hdr.ioc_number, &ioc) == -1 || !ioc)
return -ENODEV;
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- if (ioc->shost_recovery) {
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock,
- flags);
+ if (ioc->shost_recovery)
return -EAGAIN;
- }
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
memset(&karg, 0, sizeof(struct mpt2_ioctl_command));
karg.hdr.ioc_number = karg32.hdr.ioc_number;
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index 2e9a4445596..774b34525bb 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -103,7 +103,6 @@ struct sense_info {
};
-#define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF)
/**
* struct fw_event_work - firmware event struct
* @list: link list framework
@@ -1502,7 +1501,13 @@ _scsih_slave_configure(struct scsi_device *sdev)
break;
case MPI2_RAID_VOL_TYPE_RAID1E:
qdepth = MPT2SAS_RAID_QUEUE_DEPTH;
- r_level = "RAID1E";
+ if (ioc->manu_pg10.OEMIdentifier &&
+ (ioc->manu_pg10.GenericFlags0 &
+ MFG10_GF0_R10_DISPLAY) &&
+ !(raid_device->num_pds % 2))
+ r_level = "RAID10";
+ else
+ r_level = "RAID1E";
break;
case MPI2_RAID_VOL_TYPE_RAID1:
qdepth = MPT2SAS_RAID_QUEUE_DEPTH;
@@ -1786,17 +1791,18 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun,
u32 ioc_state;
unsigned long timeleft;
u8 VF_ID = 0;
- unsigned long flags;
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED ||
- ioc->shost_recovery) {
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED) {
+ printk(MPT2SAS_INFO_FMT "%s: tm_cmd busy!!!\n",
+ __func__, ioc->name);
+ return;
+ }
+
+ if (ioc->shost_recovery) {
printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
__func__, ioc->name);
return;
}
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
ioc_state = mpt2sas_base_get_iocstate(ioc, 0);
if (ioc_state & MPI2_DOORBELL_USED) {
@@ -1830,6 +1836,7 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun,
mpi_request->TaskMID = cpu_to_le16(smid_task);
int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN);
mpt2sas_scsih_set_tm_flag(ioc, handle);
+ init_completion(&ioc->tm_cmds.done);
mpt2sas_base_put_smid_hi_priority(ioc, smid, VF_ID);
timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ);
mpt2sas_scsih_clear_tm_flag(ioc, handle);
@@ -2222,7 +2229,7 @@ _scsih_ublock_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
MPT2SAS_INFO_FMT "SDEV_RUNNING: "
"handle(0x%04x)\n", ioc->name, handle));
sas_device_priv_data->block = 0;
- scsi_device_set_state(sdev, SDEV_RUNNING);
+ scsi_internal_device_unblock(sdev);
}
}
}
@@ -2251,7 +2258,7 @@ _scsih_block_io_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
MPT2SAS_INFO_FMT "SDEV_BLOCK: "
"handle(0x%04x)\n", ioc->name, handle));
sas_device_priv_data->block = 1;
- scsi_device_set_state(sdev, SDEV_BLOCK);
+ scsi_internal_device_block(sdev);
}
}
}
@@ -2327,6 +2334,7 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc,
u16 handle;
u16 reason_code;
u8 phy_number;
+ u8 link_rate;
for (i = 0; i < event_data->NumEntries; i++) {
handle = le16_to_cpu(event_data->PHY[i].AttachedDevHandle);
@@ -2337,6 +2345,11 @@ _scsih_block_io_to_children_attached_directly(struct MPT2SAS_ADAPTER *ioc,
MPI2_EVENT_SAS_TOPO_RC_MASK;
if (reason_code == MPI2_EVENT_SAS_TOPO_RC_DELAY_NOT_RESPONDING)
_scsih_block_io_device(ioc, handle);
+ if (reason_code == MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED) {
+ link_rate = event_data->PHY[i].LinkRate >> 4;
+ if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5)
+ _scsih_ublock_io_device(ioc, handle);
+ }
}
}
@@ -2405,27 +2418,6 @@ _scsih_check_topo_delete_events(struct MPT2SAS_ADAPTER *ioc,
}
/**
- * _scsih_queue_rescan - queue a topology rescan from user context
- * @ioc: per adapter object
- *
- * Return nothing.
- */
-static void
-_scsih_queue_rescan(struct MPT2SAS_ADAPTER *ioc)
-{
- struct fw_event_work *fw_event;
-
- if (ioc->wait_for_port_enable_to_complete)
- return;
- fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);
- if (!fw_event)
- return;
- fw_event->event = MPT2SAS_RESCAN_AFTER_HOST_RESET;
- fw_event->ioc = ioc;
- _scsih_fw_event_add(ioc, fw_event);
-}
-
-/**
* _scsih_flush_running_cmds - completing outstanding commands.
* @ioc: per adapter object
*
@@ -2456,46 +2448,6 @@ _scsih_flush_running_cmds(struct MPT2SAS_ADAPTER *ioc)
}
/**
- * mpt2sas_scsih_reset_handler - reset callback handler (for scsih)
- * @ioc: per adapter object
- * @reset_phase: phase
- *
- * The handler for doing any required cleanup or initialization.
- *
- * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET,
- * MPT2_IOC_DONE_RESET
- *
- * Return nothing.
- */
-void
-mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
-{
- switch (reset_phase) {
- case MPT2_IOC_PRE_RESET:
- dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
- "MPT2_IOC_PRE_RESET\n", ioc->name, __func__));
- _scsih_fw_event_off(ioc);
- break;
- case MPT2_IOC_AFTER_RESET:
- dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
- "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__));
- if (ioc->tm_cmds.status & MPT2_CMD_PENDING) {
- ioc->tm_cmds.status |= MPT2_CMD_RESET;
- mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid);
- complete(&ioc->tm_cmds.done);
- }
- _scsih_fw_event_on(ioc);
- _scsih_flush_running_cmds(ioc);
- break;
- case MPT2_IOC_DONE_RESET:
- dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
- "MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
- _scsih_queue_rescan(ioc);
- break;
- }
-}
-
-/**
* _scsih_setup_eedp - setup MPI request for EEDP transfer
* @scmd: pointer to scsi command object
* @mpi_request: pointer to the SCSI_IO reqest message frame
@@ -2615,7 +2567,6 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
Mpi2SCSIIORequest_t *mpi_request;
u32 mpi_control;
u16 smid;
- unsigned long flags;
scmd->scsi_done = done;
sas_device_priv_data = scmd->device->hostdata;
@@ -2634,13 +2585,10 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))
}
/* see if we are busy with task managment stuff */
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- if (sas_target_priv_data->tm_busy ||
- ioc->shost_recovery || ioc->ioc_link_reset_in_progress) {
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ if (sas_target_priv_data->tm_busy)
+ return SCSI_MLQUEUE_DEVICE_BUSY;
+ else if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress)
return SCSI_MLQUEUE_HOST_BUSY;
- }
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
if (scmd->sc_data_direction == DMA_FROM_DEVICE)
mpi_control = MPI2_SCSIIO_CONTROL_READ;
@@ -3189,25 +3137,6 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, u32 reply)
}
/**
- * _scsih_link_change - process phy link changes
- * @ioc: per adapter object
- * @handle: phy handle
- * @attached_handle: valid for devices attached to link
- * @phy_number: phy number
- * @link_rate: new link rate
- * Context: user.
- *
- * Return nothing.
- */
-static void
-_scsih_link_change(struct MPT2SAS_ADAPTER *ioc, u16 handle, u16 attached_handle,
- u8 phy_number, u8 link_rate)
-{
- mpt2sas_transport_update_phy_link_change(ioc, handle, attached_handle,
- phy_number, link_rate);
-}
-
-/**
* _scsih_sas_host_refresh - refreshing sas host object contents
* @ioc: per adapter object
* @update: update link information
@@ -3251,7 +3180,8 @@ _scsih_sas_host_refresh(struct MPT2SAS_ADAPTER *ioc, u8 update)
le16_to_cpu(sas_iounit_pg0->PhyData[i].
ControllerDevHandle);
if (update)
- _scsih_link_change(ioc,
+ mpt2sas_transport_update_links(
+ ioc,
ioc->sas_hba.phy[i].handle,
le16_to_cpu(sas_iounit_pg0->PhyData[i].
AttachedDevHandle), i,
@@ -3436,6 +3366,9 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle)
if (!handle)
return -1;
+ if (ioc->shost_recovery)
+ return -1;
+
if ((mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0,
MPI2_SAS_EXPAND_PGAD_FORM_HNDL, handle))) {
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
@@ -3572,6 +3505,9 @@ _scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u16 handle)
struct _sas_node *sas_expander;
unsigned long flags;
+ if (ioc->shost_recovery)
+ return;
+
spin_lock_irqsave(&ioc->sas_node_lock, flags);
sas_expander = mpt2sas_scsih_expander_find_by_handle(ioc, handle);
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
@@ -3743,6 +3679,8 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
mutex_unlock(&ioc->tm_cmds.mutex);
dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset "
"done: handle(0x%04x)\n", ioc->name, device_handle));
+ if (ioc->shost_recovery)
+ goto out;
}
/* SAS_IO_UNIT_CNTR - send REMOVE_DEVICE */
@@ -3765,6 +3703,9 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, u16 handle)
le32_to_cpu(mpi_reply.IOCLogInfo)));
out:
+
+ _scsih_ublock_io_device(ioc, handle);
+
mpt2sas_transport_port_remove(ioc, sas_device->sas_address,
sas_device->parent_handle);
@@ -3908,6 +3849,8 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
"expander event\n", ioc->name));
return;
}
+ if (ioc->shost_recovery)
+ return;
if (event_data->PHY[i].PhyStatus &
MPI2_EVENT_SAS_TOPO_PHYSTATUS_VACANT)
continue;
@@ -3923,9 +3866,10 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED:
if (!parent_handle) {
if (phy_number < ioc->sas_hba.num_phys)
- _scsih_link_change(ioc,
- ioc->sas_hba.phy[phy_number].handle,
- handle, phy_number, link_rate_);
+ mpt2sas_transport_update_links(
+ ioc,
+ ioc->sas_hba.phy[phy_number].handle,
+ handle, phy_number, link_rate_);
} else {
spin_lock_irqsave(&ioc->sas_node_lock, flags);
sas_expander =
@@ -3935,17 +3879,14 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
flags);
if (sas_expander) {
if (phy_number < sas_expander->num_phys)
- _scsih_link_change(ioc,
- sas_expander->
- phy[phy_number].handle,
- handle, phy_number,
- link_rate_);
+ mpt2sas_transport_update_links(
+ ioc,
+ sas_expander->
+ phy[phy_number].handle,
+ handle, phy_number,
+ link_rate_);
}
}
- if (reason_code == MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED) {
- if (link_rate_ >= MPI2_SAS_NEG_LINK_RATE_1_5)
- _scsih_ublock_io_device(ioc, handle);
- }
if (reason_code == MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED) {
if (link_rate_ < MPI2_SAS_NEG_LINK_RATE_1_5)
break;
@@ -4455,7 +4396,7 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc,
return;
}
- _scsih_link_change(ioc,
+ mpt2sas_transport_update_links(ioc,
le16_to_cpu(sas_device_pg0.ParentDevHandle),
handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
@@ -4744,7 +4685,7 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID,
return;
}
- _scsih_link_change(ioc,
+ mpt2sas_transport_update_links(ioc,
le16_to_cpu(sas_device_pg0.ParentDevHandle),
handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5);
@@ -5156,22 +5097,9 @@ static void
_scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)
{
struct _sas_device *sas_device, *sas_device_next;
- struct _sas_node *sas_expander, *sas_expander_next;
+ struct _sas_node *sas_expander;
struct _raid_device *raid_device, *raid_device_next;
- unsigned long flags;
- _scsih_search_responding_sas_devices(ioc);
- _scsih_search_responding_raid_devices(ioc);
- _scsih_search_responding_expanders(ioc);
-
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- ioc->shost_recovery = 0;
- if (ioc->shost->shost_state == SHOST_RECOVERY) {
- printk(MPT2SAS_INFO_FMT "putting controller into "
- "SHOST_RUNNING\n", ioc->name);
- scsi_host_set_state(ioc->shost, SHOST_RUNNING);
- }
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
list_for_each_entry_safe(sas_device, sas_device_next,
&ioc->sas_device_list, list) {
@@ -5207,16 +5135,63 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)
_scsih_raid_device_remove(ioc, raid_device);
}
- list_for_each_entry_safe(sas_expander, sas_expander_next,
- &ioc->sas_expander_list, list) {
+ retry_expander_search:
+ sas_expander = NULL;
+ list_for_each_entry(sas_expander, &ioc->sas_expander_list, list) {
if (sas_expander->responding) {
sas_expander->responding = 0;
continue;
}
- printk("\tremoving expander: handle(0x%04x), "
- " sas_addr(0x%016llx)\n", sas_expander->handle,
- (unsigned long long)sas_expander->sas_address);
_scsih_expander_remove(ioc, sas_expander->handle);
+ goto retry_expander_search;
+ }
+}
+
+/**
+ * mpt2sas_scsih_reset_handler - reset callback handler (for scsih)
+ * @ioc: per adapter object
+ * @reset_phase: phase
+ *
+ * The handler for doing any required cleanup or initialization.
+ *
+ * The reset phase can be MPT2_IOC_PRE_RESET, MPT2_IOC_AFTER_RESET,
+ * MPT2_IOC_DONE_RESET
+ *
+ * Return nothing.
+ */
+void
+mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)
+{
+ switch (reset_phase) {
+ case MPT2_IOC_PRE_RESET:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_PRE_RESET\n", ioc->name, __func__));
+ _scsih_fw_event_off(ioc);
+ break;
+ case MPT2_IOC_AFTER_RESET:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__));
+ if (ioc->tm_cmds.status & MPT2_CMD_PENDING) {
+ ioc->tm_cmds.status |= MPT2_CMD_RESET;
+ mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid);
+ complete(&ioc->tm_cmds.done);
+ }
+ _scsih_fw_event_on(ioc);
+ _scsih_flush_running_cmds(ioc);
+ break;
+ case MPT2_IOC_DONE_RESET:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_DONE_RESET\n", ioc->name, __func__));
+ _scsih_sas_host_refresh(ioc, 0);
+ _scsih_search_responding_sas_devices(ioc);
+ _scsih_search_responding_raid_devices(ioc);
+ _scsih_search_responding_expanders(ioc);
+ break;
+ case MPT2_IOC_RUNNING:
+ dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "
+ "MPT2_IOC_RUNNING\n", ioc->name, __func__));
+ _scsih_remove_unresponding_devices(ioc);
+ break;
}
}
@@ -5236,14 +5211,6 @@ _firmware_event_work(struct work_struct *work)
unsigned long flags;
struct MPT2SAS_ADAPTER *ioc = fw_event->ioc;
- /* This is invoked by calling _scsih_queue_rescan(). */
- if (fw_event->event == MPT2SAS_RESCAN_AFTER_HOST_RESET) {
- _scsih_fw_event_free(ioc, fw_event);
- _scsih_sas_host_refresh(ioc, 1);
- _scsih_remove_unresponding_devices(ioc);
- return;
- }
-
/* the queue is being flushed so ignore this event */
spin_lock_irqsave(&ioc->fw_event_lock, flags);
if (ioc->fw_events_off || ioc->remove_host) {
@@ -5253,13 +5220,10 @@ _firmware_event_work(struct work_struct *work)
}
spin_unlock_irqrestore(&ioc->fw_event_lock, flags);
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
if (ioc->shost_recovery) {
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
_scsih_fw_event_requeue(ioc, fw_event, 1000);
return;
}
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
switch (fw_event->event) {
case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST:
@@ -5461,6 +5425,8 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
if (!sas_device)
continue;
_scsih_remove_device(ioc, sas_device->handle);
+ if (ioc->shost_recovery)
+ return;
goto retry_device_search;
}
}
@@ -5482,6 +5448,8 @@ _scsih_expander_node_remove(struct MPT2SAS_ADAPTER *ioc,
if (!expander_sibling)
continue;
_scsih_expander_remove(ioc, expander_sibling->handle);
+ if (ioc->shost_recovery)
+ return;
goto retry_expander_search;
}
}
diff --git a/drivers/scsi/mpt2sas/mpt2sas_transport.c b/drivers/scsi/mpt2sas/mpt2sas_transport.c
index 686695b155c..742324a0a11 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_transport.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_transport.c
@@ -140,11 +140,18 @@ _transport_set_identify(struct MPT2SAS_ADAPTER *ioc, u16 handle,
u32 device_info;
u32 ioc_status;
+ if (ioc->shost_recovery) {
+ printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
+ __func__, ioc->name);
+ return -EFAULT;
+ }
+
if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,
MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) {
printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",
+
ioc->name, __FILE__, __LINE__, __func__);
- return -1;
+ return -ENXIO;
}
ioc_status = le16_to_cpu(mpi_reply.IOCStatus) &
@@ -153,7 +160,7 @@ _transport_set_identify(struct MPT2SAS_ADAPTER *ioc, u16 handle,
printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)"
"\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status,
__FILE__, __LINE__, __func__);
- return -1;
+ return -EIO;
}
memset(identify, 0, sizeof(identify));
@@ -288,21 +295,17 @@ _transport_expander_report_manufacture(struct MPT2SAS_ADAPTER *ioc,
void *psge;
u32 sgl_flags;
u8 issue_reset = 0;
- unsigned long flags;
void *data_out = NULL;
dma_addr_t data_out_dma;
u32 sz;
u64 *sas_address_le;
u16 wait_state_count;
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- if (ioc->ioc_reset_in_progress) {
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->shost_recovery) {
printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
__func__, ioc->name);
return -EFAULT;
}
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
mutex_lock(&ioc->transport_cmds.mutex);
@@ -789,7 +792,7 @@ mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
}
/**
- * mpt2sas_transport_update_phy_link_change - refreshing phy link changes and attached devices
+ * mpt2sas_transport_update_links - refreshing phy link changes
* @ioc: per adapter object
* @handle: handle to sas_host or expander
* @attached_handle: attached device handle
@@ -799,13 +802,19 @@ mpt2sas_transport_add_expander_phy(struct MPT2SAS_ADAPTER *ioc, struct _sas_phy
* Returns nothing.
*/
void
-mpt2sas_transport_update_phy_link_change(struct MPT2SAS_ADAPTER *ioc,
+mpt2sas_transport_update_links(struct MPT2SAS_ADAPTER *ioc,
u16 handle, u16 attached_handle, u8 phy_number, u8 link_rate)
{
unsigned long flags;
struct _sas_node *sas_node;
struct _sas_phy *mpt2sas_phy;
+ if (ioc->shost_recovery) {
+ printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
+ __func__, ioc->name);
+ return;
+ }
+
spin_lock_irqsave(&ioc->sas_node_lock, flags);
sas_node = _transport_sas_node_find_by_handle(ioc, handle);
spin_unlock_irqrestore(&ioc->sas_node_lock, flags);
@@ -1025,7 +1034,6 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
void *psge;
u32 sgl_flags;
u8 issue_reset = 0;
- unsigned long flags;
dma_addr_t dma_addr_in = 0;
dma_addr_t dma_addr_out = 0;
u16 wait_state_count;
@@ -1045,14 +1053,11 @@ _transport_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
return -EINVAL;
}
- spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags);
- if (ioc->ioc_reset_in_progress) {
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
+ if (ioc->shost_recovery) {
printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",
__func__, ioc->name);
return -EFAULT;
}
- spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags);
rc = mutex_lock_interruptible(&ioc->transport_cmds.mutex);
if (rc)
diff --git a/drivers/scsi/pcmcia/nsp_cs.c b/drivers/scsi/pcmcia/nsp_cs.c
index 70b60ade049..e32c344d7ad 100644
--- a/drivers/scsi/pcmcia/nsp_cs.c
+++ b/drivers/scsi/pcmcia/nsp_cs.c
@@ -1713,7 +1713,7 @@ static int nsp_cs_config(struct pcmcia_device *link)
nsp_dbg(NSP_DEBUG_INIT, "in");
- cfg_mem = kzalloc(sizeof(cfg_mem), GFP_KERNEL);
+ cfg_mem = kzalloc(sizeof(*cfg_mem), GFP_KERNEL);
if (!cfg_mem)
return -ENOMEM;
cfg_mem->data = data;
diff --git a/drivers/scsi/pmcraid.c b/drivers/scsi/pmcraid.c
new file mode 100644
index 00000000000..4302f06e4ec
--- /dev/null
+++ b/drivers/scsi/pmcraid.c
@@ -0,0 +1,5604 @@
+/*
+ * pmcraid.c -- driver for PMC Sierra MaxRAID controller adapters
+ *
+ * Written By: PMC Sierra Corporation
+ *
+ * Copyright (C) 2008, 2009 PMC Sierra Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA
+ *
+ */
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/hdreg.h>
+#include <linux/version.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <asm/processor.h>
+#include <linux/libata.h>
+#include <linux/mutex.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsicam.h>
+
+#include "pmcraid.h"
+
+/*
+ * Module configuration parameters
+ */
+static unsigned int pmcraid_debug_log;
+static unsigned int pmcraid_disable_aen;
+static unsigned int pmcraid_log_level = IOASC_LOG_LEVEL_MUST;
+
+/*
+ * Data structures to support multiple adapters by the LLD.
+ * pmcraid_adapter_count - count of configured adapters
+ */
+static atomic_t pmcraid_adapter_count = ATOMIC_INIT(0);
+
+/*
+ * Supporting user-level control interface through IOCTL commands.
+ * pmcraid_major - major number to use
+ * pmcraid_minor - minor number(s) to use
+ */
+static unsigned int pmcraid_major;
+static struct class *pmcraid_class;
+DECLARE_BITMAP(pmcraid_minor, PMCRAID_MAX_ADAPTERS);
+
+/*
+ * Module parameters
+ */
+MODULE_AUTHOR("PMC Sierra Corporation, anil_ravindranath@pmc-sierra.com");
+MODULE_DESCRIPTION("PMC Sierra MaxRAID Controller Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PMCRAID_DRIVER_VERSION);
+
+module_param_named(log_level, pmcraid_log_level, uint, (S_IRUGO | S_IWUSR));
+MODULE_PARM_DESC(log_level,
+ "Enables firmware error code logging, default :1 high-severity"
+ " errors, 2: all errors including high-severity errors,"
+ " 0: disables logging");
+
+module_param_named(debug, pmcraid_debug_log, uint, (S_IRUGO | S_IWUSR));
+MODULE_PARM_DESC(debug,
+ "Enable driver verbose message logging. Set 1 to enable."
+ "(default: 0)");
+
+module_param_named(disable_aen, pmcraid_disable_aen, uint, (S_IRUGO | S_IWUSR));
+MODULE_PARM_DESC(disable_aen,
+ "Disable driver aen notifications to apps. Set 1 to disable."
+ "(default: 0)");
+
+/* chip specific constants for PMC MaxRAID controllers (same for
+ * 0x5220 and 0x8010
+ */
+static struct pmcraid_chip_details pmcraid_chip_cfg[] = {
+ {
+ .ioastatus = 0x0,
+ .ioarrin = 0x00040,
+ .mailbox = 0x7FC30,
+ .global_intr_mask = 0x00034,
+ .ioa_host_intr = 0x0009C,
+ .ioa_host_intr_clr = 0x000A0,
+ .ioa_host_mask = 0x7FC28,
+ .ioa_host_mask_clr = 0x7FC28,
+ .host_ioa_intr = 0x00020,
+ .host_ioa_intr_clr = 0x00020,
+ .transop_timeout = 300
+ }
+};
+
+/*
+ * PCI device ids supported by pmcraid driver
+ */
+static struct pci_device_id pmcraid_pci_table[] __devinitdata = {
+ { PCI_DEVICE(PCI_VENDOR_ID_PMC, PCI_DEVICE_ID_PMC_MAXRAID),
+ 0, 0, (kernel_ulong_t)&pmcraid_chip_cfg[0]
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(pci, pmcraid_pci_table);
+
+
+
+/**
+ * pmcraid_slave_alloc - Prepare for commands to a device
+ * @scsi_dev: scsi device struct
+ *
+ * This function is called by mid-layer prior to sending any command to the new
+ * device. Stores resource entry details of the device in scsi_device struct.
+ * Queuecommand uses the resource handle and other details to fill up IOARCB
+ * while sending commands to the device.
+ *
+ * Return value:
+ * 0 on success / -ENXIO if device does not exist
+ */
+static int pmcraid_slave_alloc(struct scsi_device *scsi_dev)
+{
+ struct pmcraid_resource_entry *temp, *res = NULL;
+ struct pmcraid_instance *pinstance;
+ u8 target, bus, lun;
+ unsigned long lock_flags;
+ int rc = -ENXIO;
+ pinstance = shost_priv(scsi_dev->host);
+
+ /* Driver exposes VSET and GSCSI resources only; all other device types
+ * are not exposed. Resource list is synchronized using resource lock
+ * so any traversal or modifications to the list should be done inside
+ * this lock
+ */
+ spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
+ list_for_each_entry(temp, &pinstance->used_res_q, queue) {
+
+ /* do not expose VSETs with order-ids >= 240 */
+ if (RES_IS_VSET(temp->cfg_entry)) {
+ target = temp->cfg_entry.unique_flags1;
+ if (target >= PMCRAID_MAX_VSET_TARGETS)
+ continue;
+ bus = PMCRAID_VSET_BUS_ID;
+ lun = 0;
+ } else if (RES_IS_GSCSI(temp->cfg_entry)) {
+ target = RES_TARGET(temp->cfg_entry.resource_address);
+ bus = PMCRAID_PHYS_BUS_ID;
+ lun = RES_LUN(temp->cfg_entry.resource_address);
+ } else {
+ continue;
+ }
+
+ if (bus == scsi_dev->channel &&
+ target == scsi_dev->id &&
+ lun == scsi_dev->lun) {
+ res = temp;
+ break;
+ }
+ }
+
+ if (res) {
+ res->scsi_dev = scsi_dev;
+ scsi_dev->hostdata = res;
+ res->change_detected = 0;
+ atomic_set(&res->read_failures, 0);
+ atomic_set(&res->write_failures, 0);
+ rc = 0;
+ }
+ spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags);
+ return rc;
+}
+
+/**
+ * pmcraid_slave_configure - Configures a SCSI device
+ * @scsi_dev: scsi device struct
+ *
+ * This fucntion is executed by SCSI mid layer just after a device is first
+ * scanned (i.e. it has responded to an INQUIRY). For VSET resources, the
+ * timeout value (default 30s) will be over-written to a higher value (60s)
+ * and max_sectors value will be over-written to 512. It also sets queue depth
+ * to host->cmd_per_lun value
+ *
+ * Return value:
+ * 0 on success
+ */
+static int pmcraid_slave_configure(struct scsi_device *scsi_dev)
+{
+ struct pmcraid_resource_entry *res = scsi_dev->hostdata;
+
+ if (!res)
+ return 0;
+
+ /* LLD exposes VSETs and Enclosure devices only */
+ if (RES_IS_GSCSI(res->cfg_entry) &&
+ scsi_dev->type != TYPE_ENCLOSURE)
+ return -ENXIO;
+
+ pmcraid_info("configuring %x:%x:%x:%x\n",
+ scsi_dev->host->unique_id,
+ scsi_dev->channel,
+ scsi_dev->id,
+ scsi_dev->lun);
+
+ if (RES_IS_GSCSI(res->cfg_entry)) {
+ scsi_dev->allow_restart = 1;
+ } else if (RES_IS_VSET(res->cfg_entry)) {
+ scsi_dev->allow_restart = 1;
+ blk_queue_rq_timeout(scsi_dev->request_queue,
+ PMCRAID_VSET_IO_TIMEOUT);
+ blk_queue_max_sectors(scsi_dev->request_queue,
+ PMCRAID_VSET_MAX_SECTORS);
+ }
+
+ if (scsi_dev->tagged_supported &&
+ (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry))) {
+ scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth);
+ scsi_adjust_queue_depth(scsi_dev, MSG_SIMPLE_TAG,
+ scsi_dev->host->cmd_per_lun);
+ } else {
+ scsi_adjust_queue_depth(scsi_dev, 0,
+ scsi_dev->host->cmd_per_lun);
+ }
+
+ return 0;
+}
+
+/**
+ * pmcraid_slave_destroy - Unconfigure a SCSI device before removing it
+ *
+ * @scsi_dev: scsi device struct
+ *
+ * This is called by mid-layer before removing a device. Pointer assignments
+ * done in pmcraid_slave_alloc will be reset to NULL here.
+ *
+ * Return value
+ * none
+ */
+static void pmcraid_slave_destroy(struct scsi_device *scsi_dev)
+{
+ struct pmcraid_resource_entry *res;
+
+ res = (struct pmcraid_resource_entry *)scsi_dev->hostdata;
+
+ if (res)
+ res->scsi_dev = NULL;
+
+ scsi_dev->hostdata = NULL;
+}
+
+/**
+ * pmcraid_change_queue_depth - Change the device's queue depth
+ * @scsi_dev: scsi device struct
+ * @depth: depth to set
+ *
+ * Return value
+ * actual depth set
+ */
+static int pmcraid_change_queue_depth(struct scsi_device *scsi_dev, int depth)
+{
+ if (depth > PMCRAID_MAX_CMD_PER_LUN)
+ depth = PMCRAID_MAX_CMD_PER_LUN;
+
+ scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev), depth);
+
+ return scsi_dev->queue_depth;
+}
+
+/**
+ * pmcraid_change_queue_type - Change the device's queue type
+ * @scsi_dev: scsi device struct
+ * @tag: type of tags to use
+ *
+ * Return value:
+ * actual queue type set
+ */
+static int pmcraid_change_queue_type(struct scsi_device *scsi_dev, int tag)
+{
+ struct pmcraid_resource_entry *res;
+
+ res = (struct pmcraid_resource_entry *)scsi_dev->hostdata;
+
+ if ((res) && scsi_dev->tagged_supported &&
+ (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry))) {
+ scsi_set_tag_type(scsi_dev, tag);
+
+ if (tag)
+ scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth);
+ else
+ scsi_deactivate_tcq(scsi_dev, scsi_dev->queue_depth);
+ } else
+ tag = 0;
+
+ return tag;
+}
+
+
+/**
+ * pmcraid_init_cmdblk - initializes a command block
+ *
+ * @cmd: pointer to struct pmcraid_cmd to be initialized
+ * @index: if >=0 first time initialization; otherwise reinitialization
+ *
+ * Return Value
+ * None
+ */
+void pmcraid_init_cmdblk(struct pmcraid_cmd *cmd, int index)
+{
+ struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb);
+ dma_addr_t dma_addr = cmd->ioa_cb_bus_addr;
+
+ if (index >= 0) {
+ /* first time initialization (called from probe) */
+ u32 ioasa_offset =
+ offsetof(struct pmcraid_control_block, ioasa);
+
+ cmd->index = index;
+ ioarcb->response_handle = cpu_to_le32(index << 2);
+ ioarcb->ioarcb_bus_addr = cpu_to_le64(dma_addr);
+ ioarcb->ioasa_bus_addr = cpu_to_le64(dma_addr + ioasa_offset);
+ ioarcb->ioasa_len = cpu_to_le16(sizeof(struct pmcraid_ioasa));
+ } else {
+ /* re-initialization of various lengths, called once command is
+ * processed by IOA
+ */
+ memset(&cmd->ioa_cb->ioarcb.cdb, 0, PMCRAID_MAX_CDB_LEN);
+ ioarcb->request_flags0 = 0;
+ ioarcb->request_flags1 = 0;
+ ioarcb->cmd_timeout = 0;
+ ioarcb->ioarcb_bus_addr &= (~0x1FULL);
+ ioarcb->ioadl_bus_addr = 0;
+ ioarcb->ioadl_length = 0;
+ ioarcb->data_transfer_length = 0;
+ ioarcb->add_cmd_param_length = 0;
+ ioarcb->add_cmd_param_offset = 0;
+ cmd->ioa_cb->ioasa.ioasc = 0;
+ cmd->ioa_cb->ioasa.residual_data_length = 0;
+ cmd->u.time_left = 0;
+ }
+
+ cmd->cmd_done = NULL;
+ cmd->scsi_cmd = NULL;
+ cmd->release = 0;
+ cmd->completion_req = 0;
+ cmd->dma_handle = 0;
+ init_timer(&cmd->timer);
+}
+
+/**
+ * pmcraid_reinit_cmdblk - reinitialize a command block
+ *
+ * @cmd: pointer to struct pmcraid_cmd to be reinitialized
+ *
+ * Return Value
+ * None
+ */
+static void pmcraid_reinit_cmdblk(struct pmcraid_cmd *cmd)
+{
+ pmcraid_init_cmdblk(cmd, -1);
+}
+
+/**
+ * pmcraid_get_free_cmd - get a free cmd block from command block pool
+ * @pinstance: adapter instance structure
+ *
+ * Return Value:
+ * returns pointer to cmd block or NULL if no blocks are available
+ */
+static struct pmcraid_cmd *pmcraid_get_free_cmd(
+ struct pmcraid_instance *pinstance
+)
+{
+ struct pmcraid_cmd *cmd = NULL;
+ unsigned long lock_flags;
+
+ /* free cmd block list is protected by free_pool_lock */
+ spin_lock_irqsave(&pinstance->free_pool_lock, lock_flags);
+
+ if (!list_empty(&pinstance->free_cmd_pool)) {
+ cmd = list_entry(pinstance->free_cmd_pool.next,
+ struct pmcraid_cmd, free_list);
+ list_del(&cmd->free_list);
+ }
+ spin_unlock_irqrestore(&pinstance->free_pool_lock, lock_flags);
+
+ /* Initialize the command block before giving it the caller */
+ if (cmd != NULL)
+ pmcraid_reinit_cmdblk(cmd);
+ return cmd;
+}
+
+/**
+ * pmcraid_return_cmd - return a completed command block back into free pool
+ * @cmd: pointer to the command block
+ *
+ * Return Value:
+ * nothing
+ */
+void pmcraid_return_cmd(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ unsigned long lock_flags;
+
+ spin_lock_irqsave(&pinstance->free_pool_lock, lock_flags);
+ list_add_tail(&cmd->free_list, &pinstance->free_cmd_pool);
+ spin_unlock_irqrestore(&pinstance->free_pool_lock, lock_flags);
+}
+
+/**
+ * pmcraid_read_interrupts - reads IOA interrupts
+ *
+ * @pinstance: pointer to adapter instance structure
+ *
+ * Return value
+ * interrupts read from IOA
+ */
+static u32 pmcraid_read_interrupts(struct pmcraid_instance *pinstance)
+{
+ return ioread32(pinstance->int_regs.ioa_host_interrupt_reg);
+}
+
+/**
+ * pmcraid_disable_interrupts - Masks and clears all specified interrupts
+ *
+ * @pinstance: pointer to per adapter instance structure
+ * @intrs: interrupts to disable
+ *
+ * Return Value
+ * None
+ */
+static void pmcraid_disable_interrupts(
+ struct pmcraid_instance *pinstance,
+ u32 intrs
+)
+{
+ u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg);
+ u32 nmask = gmask | GLOBAL_INTERRUPT_MASK;
+
+ iowrite32(nmask, pinstance->int_regs.global_interrupt_mask_reg);
+ iowrite32(intrs, pinstance->int_regs.ioa_host_interrupt_clr_reg);
+ iowrite32(intrs, pinstance->int_regs.ioa_host_interrupt_mask_reg);
+ ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg);
+}
+
+/**
+ * pmcraid_enable_interrupts - Enables specified interrupts
+ *
+ * @pinstance: pointer to per adapter instance structure
+ * @intr: interrupts to enable
+ *
+ * Return Value
+ * None
+ */
+static void pmcraid_enable_interrupts(
+ struct pmcraid_instance *pinstance,
+ u32 intrs
+)
+{
+ u32 gmask = ioread32(pinstance->int_regs.global_interrupt_mask_reg);
+ u32 nmask = gmask & (~GLOBAL_INTERRUPT_MASK);
+
+ iowrite32(nmask, pinstance->int_regs.global_interrupt_mask_reg);
+ iowrite32(~intrs, pinstance->int_regs.ioa_host_interrupt_mask_reg);
+ ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg);
+
+ pmcraid_info("enabled interrupts global mask = %x intr_mask = %x\n",
+ ioread32(pinstance->int_regs.global_interrupt_mask_reg),
+ ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg));
+}
+
+/**
+ * pmcraid_reset_type - Determine the required reset type
+ * @pinstance: pointer to adapter instance structure
+ *
+ * IOA requires hard reset if any of the following conditions is true.
+ * 1. If HRRQ valid interrupt is not masked
+ * 2. IOA reset alert doorbell is set
+ * 3. If there are any error interrupts
+ */
+static void pmcraid_reset_type(struct pmcraid_instance *pinstance)
+{
+ u32 mask;
+ u32 intrs;
+ u32 alerts;
+
+ mask = ioread32(pinstance->int_regs.ioa_host_interrupt_mask_reg);
+ intrs = ioread32(pinstance->int_regs.ioa_host_interrupt_reg);
+ alerts = ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
+
+ if ((mask & INTRS_HRRQ_VALID) == 0 ||
+ (alerts & DOORBELL_IOA_RESET_ALERT) ||
+ (intrs & PMCRAID_ERROR_INTERRUPTS)) {
+ pmcraid_info("IOA requires hard reset\n");
+ pinstance->ioa_hard_reset = 1;
+ }
+
+ /* If unit check is active, trigger the dump */
+ if (intrs & INTRS_IOA_UNIT_CHECK)
+ pinstance->ioa_unit_check = 1;
+}
+
+/**
+ * pmcraid_bist_done - completion function for PCI BIST
+ * @cmd: pointer to reset command
+ * Return Value
+ * none
+ */
+
+static void pmcraid_ioa_reset(struct pmcraid_cmd *);
+
+static void pmcraid_bist_done(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ unsigned long lock_flags;
+ int rc;
+ u16 pci_reg;
+
+ rc = pci_read_config_word(pinstance->pdev, PCI_COMMAND, &pci_reg);
+
+ /* If PCI config space can't be accessed wait for another two secs */
+ if ((rc != PCIBIOS_SUCCESSFUL || (!(pci_reg & PCI_COMMAND_MEMORY))) &&
+ cmd->u.time_left > 0) {
+ pmcraid_info("BIST not complete, waiting another 2 secs\n");
+ cmd->timer.expires = jiffies + cmd->u.time_left;
+ cmd->u.time_left = 0;
+ cmd->timer.data = (unsigned long)cmd;
+ cmd->timer.function =
+ (void (*)(unsigned long))pmcraid_bist_done;
+ add_timer(&cmd->timer);
+ } else {
+ cmd->u.time_left = 0;
+ pmcraid_info("BIST is complete, proceeding with reset\n");
+ spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
+ pmcraid_ioa_reset(cmd);
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+ }
+}
+
+/**
+ * pmcraid_start_bist - starts BIST
+ * @cmd: pointer to reset cmd
+ * Return Value
+ * none
+ */
+static void pmcraid_start_bist(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ u32 doorbells, intrs;
+
+ /* proceed with bist and wait for 2 seconds */
+ iowrite32(DOORBELL_IOA_START_BIST,
+ pinstance->int_regs.host_ioa_interrupt_reg);
+ doorbells = ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
+ intrs = ioread32(pinstance->int_regs.ioa_host_interrupt_reg);
+ pmcraid_info("doorbells after start bist: %x intrs: %x \n",
+ doorbells, intrs);
+
+ cmd->u.time_left = msecs_to_jiffies(PMCRAID_BIST_TIMEOUT);
+ cmd->timer.data = (unsigned long)cmd;
+ cmd->timer.expires = jiffies + msecs_to_jiffies(PMCRAID_BIST_TIMEOUT);
+ cmd->timer.function = (void (*)(unsigned long))pmcraid_bist_done;
+ add_timer(&cmd->timer);
+}
+
+/**
+ * pmcraid_reset_alert_done - completion routine for reset_alert
+ * @cmd: pointer to command block used in reset sequence
+ * Return value
+ * None
+ */
+static void pmcraid_reset_alert_done(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ u32 status = ioread32(pinstance->ioa_status);
+ unsigned long lock_flags;
+
+ /* if the critical operation in progress bit is set or the wait times
+ * out, invoke reset engine to proceed with hard reset. If there is
+ * some more time to wait, restart the timer
+ */
+ if (((status & INTRS_CRITICAL_OP_IN_PROGRESS) == 0) ||
+ cmd->u.time_left <= 0) {
+ pmcraid_info("critical op is reset proceeding with reset\n");
+ spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
+ pmcraid_ioa_reset(cmd);
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+ } else {
+ pmcraid_info("critical op is not yet reset waiting again\n");
+ /* restart timer if some more time is available to wait */
+ cmd->u.time_left -= PMCRAID_CHECK_FOR_RESET_TIMEOUT;
+ cmd->timer.data = (unsigned long)cmd;
+ cmd->timer.expires = jiffies + PMCRAID_CHECK_FOR_RESET_TIMEOUT;
+ cmd->timer.function =
+ (void (*)(unsigned long))pmcraid_reset_alert_done;
+ add_timer(&cmd->timer);
+ }
+}
+
+/**
+ * pmcraid_reset_alert - alerts IOA for a possible reset
+ * @cmd : command block to be used for reset sequence.
+ *
+ * Return Value
+ * returns 0 if pci config-space is accessible and RESET_DOORBELL is
+ * successfully written to IOA. Returns non-zero in case pci_config_space
+ * is not accessible
+ */
+static void pmcraid_reset_alert(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ u32 doorbells;
+ int rc;
+ u16 pci_reg;
+
+ /* If we are able to access IOA PCI config space, alert IOA that we are
+ * going to reset it soon. This enables IOA to preserv persistent error
+ * data if any. In case memory space is not accessible, proceed with
+ * BIST or slot_reset
+ */
+ rc = pci_read_config_word(pinstance->pdev, PCI_COMMAND, &pci_reg);
+ if ((rc == PCIBIOS_SUCCESSFUL) && (pci_reg & PCI_COMMAND_MEMORY)) {
+
+ /* wait for IOA permission i.e until CRITICAL_OPERATION bit is
+ * reset IOA doesn't generate any interrupts when CRITICAL
+ * OPERATION bit is reset. A timer is started to wait for this
+ * bit to be reset.
+ */
+ cmd->u.time_left = PMCRAID_RESET_TIMEOUT;
+ cmd->timer.data = (unsigned long)cmd;
+ cmd->timer.expires = jiffies + PMCRAID_CHECK_FOR_RESET_TIMEOUT;
+ cmd->timer.function =
+ (void (*)(unsigned long))pmcraid_reset_alert_done;
+ add_timer(&cmd->timer);
+
+ iowrite32(DOORBELL_IOA_RESET_ALERT,
+ pinstance->int_regs.host_ioa_interrupt_reg);
+ doorbells =
+ ioread32(pinstance->int_regs.host_ioa_interrupt_reg);
+ pmcraid_info("doorbells after reset alert: %x\n", doorbells);
+ } else {
+ pmcraid_info("PCI config is not accessible starting BIST\n");
+ pinstance->ioa_state = IOA_STATE_IN_HARD_RESET;
+ pmcraid_start_bist(cmd);
+ }
+}
+
+/**
+ * pmcraid_timeout_handler - Timeout handler for internally generated ops
+ *
+ * @cmd : pointer to command structure, that got timedout
+ *
+ * This function blocks host requests and initiates an adapter reset.
+ *
+ * Return value:
+ * None
+ */
+static void pmcraid_timeout_handler(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ unsigned long lock_flags;
+
+ dev_err(&pinstance->pdev->dev,
+ "Adapter being reset due to command timeout.\n");
+
+ /* Command timeouts result in hard reset sequence. The command that got
+ * timed out may be the one used as part of reset sequence. In this
+ * case restart reset sequence using the same command block even if
+ * reset is in progress. Otherwise fail this command and get a free
+ * command block to restart the reset sequence.
+ */
+ spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
+ if (!pinstance->ioa_reset_in_progress) {
+ pinstance->ioa_reset_attempts = 0;
+ cmd = pmcraid_get_free_cmd(pinstance);
+
+ /* If we are out of command blocks, just return here itself.
+ * Some other command's timeout handler can do the reset job
+ */
+ if (cmd == NULL) {
+ spin_unlock_irqrestore(pinstance->host->host_lock,
+ lock_flags);
+ pmcraid_err("no free cmnd block for timeout handler\n");
+ return;
+ }
+
+ pinstance->reset_cmd = cmd;
+ pinstance->ioa_reset_in_progress = 1;
+ } else {
+ pmcraid_info("reset is already in progress\n");
+
+ if (pinstance->reset_cmd != cmd) {
+ /* This command should have been given to IOA, this
+ * command will be completed by fail_outstanding_cmds
+ * anyway
+ */
+ pmcraid_err("cmd is pending but reset in progress\n");
+ }
+
+ /* If this command was being used as part of the reset
+ * sequence, set cmd_done pointer to pmcraid_ioa_reset. This
+ * causes fail_outstanding_commands not to return the command
+ * block back to free pool
+ */
+ if (cmd == pinstance->reset_cmd)
+ cmd->cmd_done = pmcraid_ioa_reset;
+
+ }
+
+ pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
+ scsi_block_requests(pinstance->host);
+ pmcraid_reset_alert(cmd);
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+}
+
+/**
+ * pmcraid_internal_done - completion routine for internally generated cmds
+ *
+ * @cmd: command that got response from IOA
+ *
+ * Return Value:
+ * none
+ */
+static void pmcraid_internal_done(struct pmcraid_cmd *cmd)
+{
+ pmcraid_info("response internal cmd CDB[0] = %x ioasc = %x\n",
+ cmd->ioa_cb->ioarcb.cdb[0],
+ le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
+
+ /* Some of the internal commands are sent with callers blocking for the
+ * response. Same will be indicated as part of cmd->completion_req
+ * field. Response path needs to wake up any waiters waiting for cmd
+ * completion if this flag is set.
+ */
+ if (cmd->completion_req) {
+ cmd->completion_req = 0;
+ complete(&cmd->wait_for_completion);
+ }
+
+ /* most of the internal commands are completed by caller itself, so
+ * no need to return the command block back to free pool until we are
+ * required to do so (e.g once done with initialization).
+ */
+ if (cmd->release) {
+ cmd->release = 0;
+ pmcraid_return_cmd(cmd);
+ }
+}
+
+/**
+ * pmcraid_reinit_cfgtable_done - done function for cfg table reinitialization
+ *
+ * @cmd: command that got response from IOA
+ *
+ * This routine is called after driver re-reads configuration table due to a
+ * lost CCN. It returns the command block back to free pool and schedules
+ * worker thread to add/delete devices into the system.
+ *
+ * Return Value:
+ * none
+ */
+static void pmcraid_reinit_cfgtable_done(struct pmcraid_cmd *cmd)
+{
+ pmcraid_info("response internal cmd CDB[0] = %x ioasc = %x\n",
+ cmd->ioa_cb->ioarcb.cdb[0],
+ le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
+
+ if (cmd->release) {
+ cmd->release = 0;
+ pmcraid_return_cmd(cmd);
+ }
+ pmcraid_info("scheduling worker for config table reinitialization\n");
+ schedule_work(&cmd->drv_inst->worker_q);
+}
+
+/**
+ * pmcraid_erp_done - Process completion of SCSI error response from device
+ * @cmd: pmcraid_command
+ *
+ * This function copies the sense buffer into the scsi_cmd struct and completes
+ * scsi_cmd by calling scsi_done function.
+ *
+ * Return value:
+ * none
+ */
+static void pmcraid_erp_done(struct pmcraid_cmd *cmd)
+{
+ struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
+
+ if (PMCRAID_IOASC_SENSE_KEY(ioasc) > 0) {
+ scsi_cmd->result |= (DID_ERROR << 16);
+ pmcraid_err("command CDB[0] = %x failed with IOASC: 0x%08X\n",
+ cmd->ioa_cb->ioarcb.cdb[0], ioasc);
+ }
+
+ /* if we had allocated sense buffers for request sense, copy the sense
+ * release the buffers
+ */
+ if (cmd->sense_buffer != NULL) {
+ memcpy(scsi_cmd->sense_buffer,
+ cmd->sense_buffer,
+ SCSI_SENSE_BUFFERSIZE);
+ pci_free_consistent(pinstance->pdev,
+ SCSI_SENSE_BUFFERSIZE,
+ cmd->sense_buffer, cmd->sense_buffer_dma);
+ cmd->sense_buffer = NULL;
+ cmd->sense_buffer_dma = 0;
+ }
+
+ scsi_dma_unmap(scsi_cmd);
+ pmcraid_return_cmd(cmd);
+ scsi_cmd->scsi_done(scsi_cmd);
+}
+
+/**
+ * pmcraid_fire_command - sends an IOA command to adapter
+ *
+ * This function adds the given block into pending command list
+ * and returns without waiting
+ *
+ * @cmd : command to be sent to the device
+ *
+ * Return Value
+ * None
+ */
+static void _pmcraid_fire_command(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ unsigned long lock_flags;
+
+ /* Add this command block to pending cmd pool. We do this prior to
+ * writting IOARCB to ioarrin because IOA might complete the command
+ * by the time we are about to add it to the list. Response handler
+ * (isr/tasklet) looks for cmb block in the pending pending list.
+ */
+ spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags);
+ list_add_tail(&cmd->free_list, &pinstance->pending_cmd_pool);
+ spin_unlock_irqrestore(&pinstance->pending_pool_lock, lock_flags);
+ atomic_inc(&pinstance->outstanding_cmds);
+
+ /* driver writes lower 32-bit value of IOARCB address only */
+ mb();
+ iowrite32(le32_to_cpu(cmd->ioa_cb->ioarcb.ioarcb_bus_addr),
+ pinstance->ioarrin);
+}
+
+/**
+ * pmcraid_send_cmd - fires a command to IOA
+ *
+ * This function also sets up timeout function, and command completion
+ * function
+ *
+ * @cmd: pointer to the command block to be fired to IOA
+ * @cmd_done: command completion function, called once IOA responds
+ * @timeout: timeout to wait for this command completion
+ * @timeout_func: timeout handler
+ *
+ * Return value
+ * none
+ */
+static void pmcraid_send_cmd(
+ struct pmcraid_cmd *cmd,
+ void (*cmd_done) (struct pmcraid_cmd *),
+ unsigned long timeout,
+ void (*timeout_func) (struct pmcraid_cmd *)
+)
+{
+ /* initialize done function */
+ cmd->cmd_done = cmd_done;
+
+ if (timeout_func) {
+ /* setup timeout handler */
+ cmd->timer.data = (unsigned long)cmd;
+ cmd->timer.expires = jiffies + timeout;
+ cmd->timer.function = (void (*)(unsigned long))timeout_func;
+ add_timer(&cmd->timer);
+ }
+
+ /* fire the command to IOA */
+ _pmcraid_fire_command(cmd);
+}
+
+/**
+ * pmcraid_ioa_shutdown - sends SHUTDOWN command to ioa
+ *
+ * @cmd: pointer to the command block used as part of reset sequence
+ *
+ * Return Value
+ * None
+ */
+static void pmcraid_ioa_shutdown(struct pmcraid_cmd *cmd)
+{
+ pmcraid_info("response for Cancel CCN CDB[0] = %x ioasc = %x\n",
+ cmd->ioa_cb->ioarcb.cdb[0],
+ le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
+
+ /* Note that commands sent during reset require next command to be sent
+ * to IOA. Hence reinit the done function as well as timeout function
+ */
+ pmcraid_reinit_cmdblk(cmd);
+ cmd->ioa_cb->ioarcb.request_type = REQ_TYPE_IOACMD;
+ cmd->ioa_cb->ioarcb.resource_handle =
+ cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
+ cmd->ioa_cb->ioarcb.cdb[0] = PMCRAID_IOA_SHUTDOWN;
+ cmd->ioa_cb->ioarcb.cdb[1] = PMCRAID_SHUTDOWN_NORMAL;
+
+ /* fire shutdown command to hardware. */
+ pmcraid_info("firing normal shutdown command (%d) to IOA\n",
+ le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle));
+
+ pmcraid_send_cmd(cmd, pmcraid_ioa_reset,
+ PMCRAID_SHUTDOWN_TIMEOUT,
+ pmcraid_timeout_handler);
+}
+
+/**
+ * pmcraid_identify_hrrq - registers host rrq buffers with IOA
+ * @cmd: pointer to command block to be used for identify hrrq
+ *
+ * Return Value
+ * 0 in case of success, otherwise non-zero failure code
+ */
+
+static void pmcraid_querycfg(struct pmcraid_cmd *);
+
+static void pmcraid_identify_hrrq(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
+ int index = 0;
+ __be64 hrrq_addr = cpu_to_be64(pinstance->hrrq_start_bus_addr[index]);
+ u32 hrrq_size = cpu_to_be32(sizeof(u32) * PMCRAID_MAX_CMD);
+
+ pmcraid_reinit_cmdblk(cmd);
+
+ /* Initialize ioarcb */
+ ioarcb->request_type = REQ_TYPE_IOACMD;
+ ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
+
+ /* initialize the hrrq number where IOA will respond to this command */
+ ioarcb->hrrq_id = index;
+ ioarcb->cdb[0] = PMCRAID_IDENTIFY_HRRQ;
+ ioarcb->cdb[1] = index;
+
+ /* IOA expects 64-bit pci address to be written in B.E format
+ * (i.e cdb[2]=MSByte..cdb[9]=LSB.
+ */
+ pmcraid_info("HRRQ_IDENTIFY with hrrq:ioarcb => %llx:%llx\n",
+ hrrq_addr, ioarcb->ioarcb_bus_addr);
+
+ memcpy(&(ioarcb->cdb[2]), &hrrq_addr, sizeof(hrrq_addr));
+ memcpy(&(ioarcb->cdb[10]), &hrrq_size, sizeof(hrrq_size));
+
+ /* Subsequent commands require HRRQ identification to be successful.
+ * Note that this gets called even during reset from SCSI mid-layer
+ * or tasklet
+ */
+ pmcraid_send_cmd(cmd, pmcraid_querycfg,
+ PMCRAID_INTERNAL_TIMEOUT,
+ pmcraid_timeout_handler);
+}
+
+static void pmcraid_process_ccn(struct pmcraid_cmd *cmd);
+static void pmcraid_process_ldn(struct pmcraid_cmd *cmd);
+
+/**
+ * pmcraid_send_hcam_cmd - send an initialized command block(HCAM) to IOA
+ *
+ * @cmd: initialized command block pointer
+ *
+ * Return Value
+ * none
+ */
+static void pmcraid_send_hcam_cmd(struct pmcraid_cmd *cmd)
+{
+ if (cmd->ioa_cb->ioarcb.cdb[1] == PMCRAID_HCAM_CODE_CONFIG_CHANGE)
+ atomic_set(&(cmd->drv_inst->ccn.ignore), 0);
+ else
+ atomic_set(&(cmd->drv_inst->ldn.ignore), 0);
+
+ pmcraid_send_cmd(cmd, cmd->cmd_done, 0, NULL);
+}
+
+/**
+ * pmcraid_init_hcam - send an initialized command block(HCAM) to IOA
+ *
+ * @pinstance: pointer to adapter instance structure
+ * @type: HCAM type
+ *
+ * Return Value
+ * pointer to initialized pmcraid_cmd structure or NULL
+ */
+static struct pmcraid_cmd *pmcraid_init_hcam
+(
+ struct pmcraid_instance *pinstance,
+ u8 type
+)
+{
+ struct pmcraid_cmd *cmd;
+ struct pmcraid_ioarcb *ioarcb;
+ struct pmcraid_ioadl_desc *ioadl;
+ struct pmcraid_hostrcb *hcam;
+ void (*cmd_done) (struct pmcraid_cmd *);
+ dma_addr_t dma;
+ int rcb_size;
+
+ cmd = pmcraid_get_free_cmd(pinstance);
+
+ if (!cmd) {
+ pmcraid_err("no free command blocks for hcam\n");
+ return cmd;
+ }
+
+ if (type == PMCRAID_HCAM_CODE_CONFIG_CHANGE) {
+ rcb_size = sizeof(struct pmcraid_hcam_ccn);
+ cmd_done = pmcraid_process_ccn;
+ dma = pinstance->ccn.baddr + PMCRAID_AEN_HDR_SIZE;
+ hcam = &pinstance->ccn;
+ } else {
+ rcb_size = sizeof(struct pmcraid_hcam_ldn);
+ cmd_done = pmcraid_process_ldn;
+ dma = pinstance->ldn.baddr + PMCRAID_AEN_HDR_SIZE;
+ hcam = &pinstance->ldn;
+ }
+
+ /* initialize command pointer used for HCAM registration */
+ hcam->cmd = cmd;
+
+ ioarcb = &cmd->ioa_cb->ioarcb;
+ ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) +
+ offsetof(struct pmcraid_ioarcb,
+ add_data.u.ioadl[0]));
+ ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
+ ioadl = ioarcb->add_data.u.ioadl;
+
+ /* Initialize ioarcb */
+ ioarcb->request_type = REQ_TYPE_HCAM;
+ ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
+ ioarcb->cdb[0] = PMCRAID_HOST_CONTROLLED_ASYNC;
+ ioarcb->cdb[1] = type;
+ ioarcb->cdb[7] = (rcb_size >> 8) & 0xFF;
+ ioarcb->cdb[8] = (rcb_size) & 0xFF;
+
+ ioarcb->data_transfer_length = cpu_to_le32(rcb_size);
+
+ ioadl[0].flags |= cpu_to_le32(IOADL_FLAGS_READ_LAST);
+ ioadl[0].data_len = cpu_to_le32(rcb_size);
+ ioadl[0].address = cpu_to_le32(dma);
+
+ cmd->cmd_done = cmd_done;
+ return cmd;
+}
+
+/**
+ * pmcraid_send_hcam - Send an HCAM to IOA
+ * @pinstance: ioa config struct
+ * @type: HCAM type
+ *
+ * This function will send a Host Controlled Async command to IOA.
+ *
+ * Return value:
+ * none
+ */
+static void pmcraid_send_hcam(struct pmcraid_instance *pinstance, u8 type)
+{
+ struct pmcraid_cmd *cmd = pmcraid_init_hcam(pinstance, type);
+ pmcraid_send_hcam_cmd(cmd);
+}
+
+
+/**
+ * pmcraid_prepare_cancel_cmd - prepares a command block to abort another
+ *
+ * @cmd: pointer to cmd that is used as cancelling command
+ * @cmd_to_cancel: pointer to the command that needs to be cancelled
+ */
+static void pmcraid_prepare_cancel_cmd(
+ struct pmcraid_cmd *cmd,
+ struct pmcraid_cmd *cmd_to_cancel
+)
+{
+ struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
+ __be64 ioarcb_addr = cmd_to_cancel->ioa_cb->ioarcb.ioarcb_bus_addr;
+
+ /* Get the resource handle to where the command to be aborted has been
+ * sent.
+ */
+ ioarcb->resource_handle = cmd_to_cancel->ioa_cb->ioarcb.resource_handle;
+ ioarcb->request_type = REQ_TYPE_IOACMD;
+ memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN);
+ ioarcb->cdb[0] = PMCRAID_ABORT_CMD;
+
+ /* IOARCB address of the command to be cancelled is given in
+ * cdb[2]..cdb[9] is Big-Endian format. Note that length bits in
+ * IOARCB address are not masked.
+ */
+ ioarcb_addr = cpu_to_be64(ioarcb_addr);
+ memcpy(&(ioarcb->cdb[2]), &ioarcb_addr, sizeof(ioarcb_addr));
+}
+
+/**
+ * pmcraid_cancel_hcam - sends ABORT task to abort a given HCAM
+ *
+ * @cmd: command to be used as cancelling command
+ * @type: HCAM type
+ * @cmd_done: op done function for the cancelling command
+ */
+static void pmcraid_cancel_hcam(
+ struct pmcraid_cmd *cmd,
+ u8 type,
+ void (*cmd_done) (struct pmcraid_cmd *)
+)
+{
+ struct pmcraid_instance *pinstance;
+ struct pmcraid_hostrcb *hcam;
+
+ pinstance = cmd->drv_inst;
+ hcam = (type == PMCRAID_HCAM_CODE_LOG_DATA) ?
+ &pinstance->ldn : &pinstance->ccn;
+
+ /* prepare for cancelling previous hcam command. If the HCAM is
+ * currently not pending with IOA, we would have hcam->cmd as non-null
+ */
+ if (hcam->cmd == NULL)
+ return;
+
+ pmcraid_prepare_cancel_cmd(cmd, hcam->cmd);
+
+ /* writing to IOARRIN must be protected by host_lock, as mid-layer
+ * schedule queuecommand while we are doing this
+ */
+ pmcraid_send_cmd(cmd, cmd_done,
+ PMCRAID_INTERNAL_TIMEOUT,
+ pmcraid_timeout_handler);
+}
+
+/**
+ * pmcraid_cancel_ccn - cancel CCN HCAM already registered with IOA
+ *
+ * @cmd: command block to be used for cancelling the HCAM
+ */
+static void pmcraid_cancel_ccn(struct pmcraid_cmd *cmd)
+{
+ pmcraid_info("response for Cancel LDN CDB[0] = %x ioasc = %x\n",
+ cmd->ioa_cb->ioarcb.cdb[0],
+ le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
+
+ pmcraid_reinit_cmdblk(cmd);
+
+ pmcraid_cancel_hcam(cmd,
+ PMCRAID_HCAM_CODE_CONFIG_CHANGE,
+ pmcraid_ioa_shutdown);
+}
+
+/**
+ * pmcraid_cancel_ldn - cancel LDN HCAM already registered with IOA
+ *
+ * @cmd: command block to be used for cancelling the HCAM
+ */
+static void pmcraid_cancel_ldn(struct pmcraid_cmd *cmd)
+{
+ pmcraid_cancel_hcam(cmd,
+ PMCRAID_HCAM_CODE_LOG_DATA,
+ pmcraid_cancel_ccn);
+}
+
+/**
+ * pmcraid_expose_resource - check if the resource can be exposed to OS
+ *
+ * @cfgte: pointer to configuration table entry of the resource
+ *
+ * Return value:
+ * true if resource can be added to midlayer, false(0) otherwise
+ */
+static int pmcraid_expose_resource(struct pmcraid_config_table_entry *cfgte)
+{
+ int retval = 0;
+
+ if (cfgte->resource_type == RES_TYPE_VSET)
+ retval = ((cfgte->unique_flags1 & 0xFF) < 0xFE);
+ else if (cfgte->resource_type == RES_TYPE_GSCSI)
+ retval = (RES_BUS(cfgte->resource_address) !=
+ PMCRAID_VIRTUAL_ENCL_BUS_ID);
+ return retval;
+}
+
+/* attributes supported by pmcraid_event_family */
+enum {
+ PMCRAID_AEN_ATTR_UNSPEC,
+ PMCRAID_AEN_ATTR_EVENT,
+ __PMCRAID_AEN_ATTR_MAX,
+};
+#define PMCRAID_AEN_ATTR_MAX (__PMCRAID_AEN_ATTR_MAX - 1)
+
+/* commands supported by pmcraid_event_family */
+enum {
+ PMCRAID_AEN_CMD_UNSPEC,
+ PMCRAID_AEN_CMD_EVENT,
+ __PMCRAID_AEN_CMD_MAX,
+};
+#define PMCRAID_AEN_CMD_MAX (__PMCRAID_AEN_CMD_MAX - 1)
+
+static struct genl_family pmcraid_event_family = {
+ .id = GENL_ID_GENERATE,
+ .name = "pmcraid",
+ .version = 1,
+ .maxattr = PMCRAID_AEN_ATTR_MAX
+};
+
+/**
+ * pmcraid_netlink_init - registers pmcraid_event_family
+ *
+ * Return value:
+ * 0 if the pmcraid_event_family is successfully registered
+ * with netlink generic, non-zero otherwise
+ */
+static int pmcraid_netlink_init(void)
+{
+ int result;
+
+ result = genl_register_family(&pmcraid_event_family);
+
+ if (result)
+ return result;
+
+ pmcraid_info("registered NETLINK GENERIC group: %d\n",
+ pmcraid_event_family.id);
+
+ return result;
+}
+
+/**
+ * pmcraid_netlink_release - unregisters pmcraid_event_family
+ *
+ * Return value:
+ * none
+ */
+static void pmcraid_netlink_release(void)
+{
+ genl_unregister_family(&pmcraid_event_family);
+}
+
+/**
+ * pmcraid_notify_aen - sends event msg to user space application
+ * @pinstance: pointer to adapter instance structure
+ * @type: HCAM type
+ *
+ * Return value:
+ * 0 if success, error value in case of any failure.
+ */
+static int pmcraid_notify_aen(struct pmcraid_instance *pinstance, u8 type)
+{
+ struct sk_buff *skb;
+ struct pmcraid_aen_msg *aen_msg;
+ void *msg_header;
+ int data_size, total_size;
+ int result;
+
+
+ if (type == PMCRAID_HCAM_CODE_LOG_DATA) {
+ aen_msg = pinstance->ldn.msg;
+ data_size = pinstance->ldn.hcam->data_len;
+ } else {
+ aen_msg = pinstance->ccn.msg;
+ data_size = pinstance->ccn.hcam->data_len;
+ }
+
+ data_size += sizeof(struct pmcraid_hcam_hdr);
+ aen_msg->hostno = (pinstance->host->unique_id << 16 |
+ MINOR(pinstance->cdev.dev));
+ aen_msg->length = data_size;
+ data_size += sizeof(*aen_msg);
+
+ total_size = nla_total_size(data_size);
+ skb = genlmsg_new(total_size, GFP_ATOMIC);
+
+
+ if (!skb) {
+ pmcraid_err("Failed to allocate aen data SKB of size: %x\n",
+ total_size);
+ return -ENOMEM;
+ }
+
+ /* add the genetlink message header */
+ msg_header = genlmsg_put(skb, 0, 0,
+ &pmcraid_event_family, 0,
+ PMCRAID_AEN_CMD_EVENT);
+ if (!msg_header) {
+ pmcraid_err("failed to copy command details\n");
+ nlmsg_free(skb);
+ return -ENOMEM;
+ }
+
+ result = nla_put(skb, PMCRAID_AEN_ATTR_EVENT, data_size, aen_msg);
+
+ if (result) {
+ pmcraid_err("failed to copy AEN attribute data \n");
+ nlmsg_free(skb);
+ return -EINVAL;
+ }
+
+ /* send genetlink multicast message to notify appplications */
+ result = genlmsg_end(skb, msg_header);
+
+ if (result < 0) {
+ pmcraid_err("genlmsg_end failed\n");
+ nlmsg_free(skb);
+ return result;
+ }
+
+ result =
+ genlmsg_multicast(skb, 0, pmcraid_event_family.id, GFP_ATOMIC);
+
+ /* If there are no listeners, genlmsg_multicast may return non-zero
+ * value.
+ */
+ if (result)
+ pmcraid_info("failed to send %s event message %x!\n",
+ type == PMCRAID_HCAM_CODE_LOG_DATA ? "LDN" : "CCN",
+ result);
+ return result;
+}
+
+/**
+ * pmcraid_handle_config_change - Handle a config change from the adapter
+ * @pinstance: pointer to per adapter instance structure
+ *
+ * Return value:
+ * none
+ */
+static void pmcraid_handle_config_change(struct pmcraid_instance *pinstance)
+{
+ struct pmcraid_config_table_entry *cfg_entry;
+ struct pmcraid_hcam_ccn *ccn_hcam;
+ struct pmcraid_cmd *cmd;
+ struct pmcraid_cmd *cfgcmd;
+ struct pmcraid_resource_entry *res = NULL;
+ u32 new_entry = 1;
+ unsigned long lock_flags;
+ unsigned long host_lock_flags;
+ int rc;
+
+ ccn_hcam = (struct pmcraid_hcam_ccn *)pinstance->ccn.hcam;
+ cfg_entry = &ccn_hcam->cfg_entry;
+
+ pmcraid_info
+ ("CCN(%x): %x type: %x lost: %x flags: %x res: %x:%x:%x:%x\n",
+ pinstance->ccn.hcam->ilid,
+ pinstance->ccn.hcam->op_code,
+ pinstance->ccn.hcam->notification_type,
+ pinstance->ccn.hcam->notification_lost,
+ pinstance->ccn.hcam->flags,
+ pinstance->host->unique_id,
+ RES_IS_VSET(*cfg_entry) ? PMCRAID_VSET_BUS_ID :
+ (RES_IS_GSCSI(*cfg_entry) ? PMCRAID_PHYS_BUS_ID :
+ RES_BUS(cfg_entry->resource_address)),
+ RES_IS_VSET(*cfg_entry) ? cfg_entry->unique_flags1 :
+ RES_TARGET(cfg_entry->resource_address),
+ RES_LUN(cfg_entry->resource_address));
+
+
+ /* If this HCAM indicates a lost notification, read the config table */
+ if (pinstance->ccn.hcam->notification_lost) {
+ cfgcmd = pmcraid_get_free_cmd(pinstance);
+ if (cfgcmd) {
+ pmcraid_info("lost CCN, reading config table\b");
+ pinstance->reinit_cfg_table = 1;
+ pmcraid_querycfg(cfgcmd);
+ } else {
+ pmcraid_err("lost CCN, no free cmd for querycfg\n");
+ }
+ goto out_notify_apps;
+ }
+
+ /* If this resource is not going to be added to mid-layer, just notify
+ * applications and return
+ */
+ if (!pmcraid_expose_resource(cfg_entry))
+ goto out_notify_apps;
+
+ spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
+ list_for_each_entry(res, &pinstance->used_res_q, queue) {
+ rc = memcmp(&res->cfg_entry.resource_address,
+ &cfg_entry->resource_address,
+ sizeof(cfg_entry->resource_address));
+ if (!rc) {
+ new_entry = 0;
+ break;
+ }
+ }
+
+ if (new_entry) {
+
+ /* If there are more number of resources than what driver can
+ * manage, do not notify the applications about the CCN. Just
+ * ignore this notifications and re-register the same HCAM
+ */
+ if (list_empty(&pinstance->free_res_q)) {
+ spin_unlock_irqrestore(&pinstance->resource_lock,
+ lock_flags);
+ pmcraid_err("too many resources attached\n");
+ spin_lock_irqsave(pinstance->host->host_lock,
+ host_lock_flags);
+ pmcraid_send_hcam(pinstance,
+ PMCRAID_HCAM_CODE_CONFIG_CHANGE);
+ spin_unlock_irqrestore(pinstance->host->host_lock,
+ host_lock_flags);
+ return;
+ }
+
+ res = list_entry(pinstance->free_res_q.next,
+ struct pmcraid_resource_entry, queue);
+
+ list_del(&res->queue);
+ res->scsi_dev = NULL;
+ res->reset_progress = 0;
+ list_add_tail(&res->queue, &pinstance->used_res_q);
+ }
+
+ memcpy(&res->cfg_entry, cfg_entry,
+ sizeof(struct pmcraid_config_table_entry));
+
+ if (pinstance->ccn.hcam->notification_type ==
+ NOTIFICATION_TYPE_ENTRY_DELETED) {
+ if (res->scsi_dev) {
+ res->change_detected = RES_CHANGE_DEL;
+ res->cfg_entry.resource_handle =
+ PMCRAID_INVALID_RES_HANDLE;
+ schedule_work(&pinstance->worker_q);
+ } else {
+ /* This may be one of the non-exposed resources */
+ list_move_tail(&res->queue, &pinstance->free_res_q);
+ }
+ } else if (!res->scsi_dev) {
+ res->change_detected = RES_CHANGE_ADD;
+ schedule_work(&pinstance->worker_q);
+ }
+ spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags);
+
+out_notify_apps:
+
+ /* Notify configuration changes to registered applications.*/
+ if (!pmcraid_disable_aen)
+ pmcraid_notify_aen(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE);
+
+ cmd = pmcraid_init_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE);
+ if (cmd)
+ pmcraid_send_hcam_cmd(cmd);
+}
+
+/**
+ * pmcraid_get_error_info - return error string for an ioasc
+ * @ioasc: ioasc code
+ * Return Value
+ * none
+ */
+static struct pmcraid_ioasc_error *pmcraid_get_error_info(u32 ioasc)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(pmcraid_ioasc_error_table); i++) {
+ if (pmcraid_ioasc_error_table[i].ioasc_code == ioasc)
+ return &pmcraid_ioasc_error_table[i];
+ }
+ return NULL;
+}
+
+/**
+ * pmcraid_ioasc_logger - log IOASC information based user-settings
+ * @ioasc: ioasc code
+ * @cmd: pointer to command that resulted in 'ioasc'
+ */
+void pmcraid_ioasc_logger(u32 ioasc, struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_ioasc_error *error_info = pmcraid_get_error_info(ioasc);
+
+ if (error_info == NULL ||
+ cmd->drv_inst->current_log_level < error_info->log_level)
+ return;
+
+ /* log the error string */
+ pmcraid_err("cmd [%d] for resource %x failed with %x(%s)\n",
+ cmd->ioa_cb->ioarcb.cdb[0],
+ cmd->ioa_cb->ioarcb.resource_handle,
+ le32_to_cpu(ioasc), error_info->error_string);
+}
+
+/**
+ * pmcraid_handle_error_log - Handle a config change (error log) from the IOA
+ *
+ * @pinstance: pointer to per adapter instance structure
+ *
+ * Return value:
+ * none
+ */
+static void pmcraid_handle_error_log(struct pmcraid_instance *pinstance)
+{
+ struct pmcraid_hcam_ldn *hcam_ldn;
+ u32 ioasc;
+
+ hcam_ldn = (struct pmcraid_hcam_ldn *)pinstance->ldn.hcam;
+
+ pmcraid_info
+ ("LDN(%x): %x type: %x lost: %x flags: %x overlay id: %x\n",
+ pinstance->ldn.hcam->ilid,
+ pinstance->ldn.hcam->op_code,
+ pinstance->ldn.hcam->notification_type,
+ pinstance->ldn.hcam->notification_lost,
+ pinstance->ldn.hcam->flags,
+ pinstance->ldn.hcam->overlay_id);
+
+ /* log only the errors, no need to log informational log entries */
+ if (pinstance->ldn.hcam->notification_type !=
+ NOTIFICATION_TYPE_ERROR_LOG)
+ return;
+
+ if (pinstance->ldn.hcam->notification_lost ==
+ HOSTRCB_NOTIFICATIONS_LOST)
+ dev_err(&pinstance->pdev->dev, "Error notifications lost\n");
+
+ ioasc = le32_to_cpu(hcam_ldn->error_log.fd_ioasc);
+
+ if (ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET ||
+ ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET_BY_OTHER) {
+ dev_err(&pinstance->pdev->dev,
+ "UnitAttention due to IOA Bus Reset\n");
+ scsi_report_bus_reset(
+ pinstance->host,
+ RES_BUS(hcam_ldn->error_log.fd_ra));
+ }
+
+ return;
+}
+
+/**
+ * pmcraid_process_ccn - Op done function for a CCN.
+ * @cmd: pointer to command struct
+ *
+ * This function is the op done function for a configuration
+ * change notification
+ *
+ * Return value:
+ * none
+ */
+static void pmcraid_process_ccn(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
+ unsigned long lock_flags;
+
+ pinstance->ccn.cmd = NULL;
+ pmcraid_return_cmd(cmd);
+
+ /* If driver initiated IOA reset happened while this hcam was pending
+ * with IOA, or IOA bringdown sequence is in progress, no need to
+ * re-register the hcam
+ */
+ if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET ||
+ atomic_read(&pinstance->ccn.ignore) == 1) {
+ return;
+ } else if (ioasc) {
+ dev_err(&pinstance->pdev->dev,
+ "Host RCB (CCN) failed with IOASC: 0x%08X\n", ioasc);
+ spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
+ pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE);
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+ } else {
+ pmcraid_handle_config_change(pinstance);
+ }
+}
+
+/**
+ * pmcraid_process_ldn - op done function for an LDN
+ * @cmd: pointer to command block
+ *
+ * Return value
+ * none
+ */
+static void pmcraid_initiate_reset(struct pmcraid_instance *);
+
+static void pmcraid_process_ldn(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ struct pmcraid_hcam_ldn *ldn_hcam =
+ (struct pmcraid_hcam_ldn *)pinstance->ldn.hcam;
+ u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
+ u32 fd_ioasc = le32_to_cpu(ldn_hcam->error_log.fd_ioasc);
+ unsigned long lock_flags;
+
+ /* return the command block back to freepool */
+ pinstance->ldn.cmd = NULL;
+ pmcraid_return_cmd(cmd);
+
+ /* If driver initiated IOA reset happened while this hcam was pending
+ * with IOA, no need to re-register the hcam as reset engine will do it
+ * once reset sequence is complete
+ */
+ if (ioasc == PMCRAID_IOASC_IOA_WAS_RESET ||
+ atomic_read(&pinstance->ccn.ignore) == 1) {
+ return;
+ } else if (!ioasc) {
+ pmcraid_handle_error_log(pinstance);
+ if (fd_ioasc == PMCRAID_IOASC_NR_IOA_RESET_REQUIRED) {
+ spin_lock_irqsave(pinstance->host->host_lock,
+ lock_flags);
+ pmcraid_initiate_reset(pinstance);
+ spin_unlock_irqrestore(pinstance->host->host_lock,
+ lock_flags);
+ return;
+ }
+ } else {
+ dev_err(&pinstance->pdev->dev,
+ "Host RCB(LDN) failed with IOASC: 0x%08X\n", ioasc);
+ }
+ /* send netlink message for HCAM notification if enabled */
+ if (!pmcraid_disable_aen)
+ pmcraid_notify_aen(pinstance, PMCRAID_HCAM_CODE_LOG_DATA);
+
+ cmd = pmcraid_init_hcam(pinstance, PMCRAID_HCAM_CODE_LOG_DATA);
+ if (cmd)
+ pmcraid_send_hcam_cmd(cmd);
+}
+
+/**
+ * pmcraid_register_hcams - register HCAMs for CCN and LDN
+ *
+ * @pinstance: pointer per adapter instance structure
+ *
+ * Return Value
+ * none
+ */
+static void pmcraid_register_hcams(struct pmcraid_instance *pinstance)
+{
+ pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_CONFIG_CHANGE);
+ pmcraid_send_hcam(pinstance, PMCRAID_HCAM_CODE_LOG_DATA);
+}
+
+/**
+ * pmcraid_unregister_hcams - cancel HCAMs registered already
+ * @cmd: pointer to command used as part of reset sequence
+ */
+static void pmcraid_unregister_hcams(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+
+ /* During IOA bringdown, HCAM gets fired and tasklet proceeds with
+ * handling hcam response though it is not necessary. In order to
+ * prevent this, set 'ignore', so that bring-down sequence doesn't
+ * re-send any more hcams
+ */
+ atomic_set(&pinstance->ccn.ignore, 1);
+ atomic_set(&pinstance->ldn.ignore, 1);
+
+ /* If adapter reset was forced as part of runtime reset sequence,
+ * start the reset sequence.
+ */
+ if (pinstance->force_ioa_reset && !pinstance->ioa_bringdown) {
+ pinstance->force_ioa_reset = 0;
+ pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
+ pmcraid_reset_alert(cmd);
+ return;
+ }
+
+ /* Driver tries to cancel HCAMs by sending ABORT TASK for each HCAM
+ * one after the other. So CCN cancellation will be triggered by
+ * pmcraid_cancel_ldn itself.
+ */
+ pmcraid_cancel_ldn(cmd);
+}
+
+/**
+ * pmcraid_reset_enable_ioa - re-enable IOA after a hard reset
+ * @pinstance: pointer to adapter instance structure
+ * Return Value
+ * 1 if TRANSITION_TO_OPERATIONAL is active, otherwise 0
+ */
+static void pmcraid_reinit_buffers(struct pmcraid_instance *);
+
+static int pmcraid_reset_enable_ioa(struct pmcraid_instance *pinstance)
+{
+ u32 intrs;
+
+ pmcraid_reinit_buffers(pinstance);
+ intrs = pmcraid_read_interrupts(pinstance);
+
+ pmcraid_enable_interrupts(pinstance, PMCRAID_PCI_INTERRUPTS);
+
+ if (intrs & INTRS_TRANSITION_TO_OPERATIONAL) {
+ iowrite32(INTRS_TRANSITION_TO_OPERATIONAL,
+ pinstance->int_regs.ioa_host_interrupt_mask_reg);
+ iowrite32(INTRS_TRANSITION_TO_OPERATIONAL,
+ pinstance->int_regs.ioa_host_interrupt_clr_reg);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * pmcraid_soft_reset - performs a soft reset and makes IOA become ready
+ * @cmd : pointer to reset command block
+ *
+ * Return Value
+ * none
+ */
+static void pmcraid_soft_reset(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ u32 int_reg;
+ u32 doorbell;
+
+ /* There will be an interrupt when Transition to Operational bit is
+ * set so tasklet would execute next reset task. The timeout handler
+ * would re-initiate a reset
+ */
+ cmd->cmd_done = pmcraid_ioa_reset;
+ cmd->timer.data = (unsigned long)cmd;
+ cmd->timer.expires = jiffies +
+ msecs_to_jiffies(PMCRAID_TRANSOP_TIMEOUT);
+ cmd->timer.function = (void (*)(unsigned long))pmcraid_timeout_handler;
+
+ if (!timer_pending(&cmd->timer))
+ add_timer(&cmd->timer);
+
+ /* Enable destructive diagnostics on IOA if it is not yet in
+ * operational state
+ */
+ doorbell = DOORBELL_RUNTIME_RESET |
+ DOORBELL_ENABLE_DESTRUCTIVE_DIAGS;
+
+ iowrite32(doorbell, pinstance->int_regs.host_ioa_interrupt_reg);
+ int_reg = ioread32(pinstance->int_regs.ioa_host_interrupt_reg);
+ pmcraid_info("Waiting for IOA to become operational %x:%x\n",
+ ioread32(pinstance->int_regs.host_ioa_interrupt_reg),
+ int_reg);
+}
+
+/**
+ * pmcraid_get_dump - retrieves IOA dump in case of Unit Check interrupt
+ *
+ * @pinstance: pointer to adapter instance structure
+ *
+ * Return Value
+ * none
+ */
+static void pmcraid_get_dump(struct pmcraid_instance *pinstance)
+{
+ pmcraid_info("%s is not yet implemented\n", __func__);
+}
+
+/**
+ * pmcraid_fail_outstanding_cmds - Fails all outstanding ops.
+ * @pinstance: pointer to adapter instance structure
+ *
+ * This function fails all outstanding ops. If they are submitted to IOA
+ * already, it sends cancel all messages if IOA is still accepting IOARCBs,
+ * otherwise just completes the commands and returns the cmd blocks to free
+ * pool.
+ *
+ * Return value:
+ * none
+ */
+static void pmcraid_fail_outstanding_cmds(struct pmcraid_instance *pinstance)
+{
+ struct pmcraid_cmd *cmd, *temp;
+ unsigned long lock_flags;
+
+ /* pending command list is protected by pending_pool_lock. Its
+ * traversal must be done as within this lock
+ */
+ spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags);
+ list_for_each_entry_safe(cmd, temp, &pinstance->pending_cmd_pool,
+ free_list) {
+ list_del(&cmd->free_list);
+ spin_unlock_irqrestore(&pinstance->pending_pool_lock,
+ lock_flags);
+ cmd->ioa_cb->ioasa.ioasc =
+ cpu_to_le32(PMCRAID_IOASC_IOA_WAS_RESET);
+ cmd->ioa_cb->ioasa.ilid =
+ cpu_to_be32(PMCRAID_DRIVER_ILID);
+
+ /* In case the command timer is still running */
+ del_timer(&cmd->timer);
+
+ /* If this is an IO command, complete it by invoking scsi_done
+ * function. If this is one of the internal commands other
+ * than pmcraid_ioa_reset and HCAM commands invoke cmd_done to
+ * complete it
+ */
+ if (cmd->scsi_cmd) {
+
+ struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
+ __le32 resp = cmd->ioa_cb->ioarcb.response_handle;
+
+ scsi_cmd->result |= DID_ERROR << 16;
+
+ scsi_dma_unmap(scsi_cmd);
+ pmcraid_return_cmd(cmd);
+
+
+ pmcraid_info("failing(%d) CDB[0] = %x result: %x\n",
+ le32_to_cpu(resp) >> 2,
+ cmd->ioa_cb->ioarcb.cdb[0],
+ scsi_cmd->result);
+ scsi_cmd->scsi_done(scsi_cmd);
+ } else if (cmd->cmd_done == pmcraid_internal_done ||
+ cmd->cmd_done == pmcraid_erp_done) {
+ cmd->cmd_done(cmd);
+ } else if (cmd->cmd_done != pmcraid_ioa_reset) {
+ pmcraid_return_cmd(cmd);
+ }
+
+ atomic_dec(&pinstance->outstanding_cmds);
+ spin_lock_irqsave(&pinstance->pending_pool_lock, lock_flags);
+ }
+
+ spin_unlock_irqrestore(&pinstance->pending_pool_lock, lock_flags);
+}
+
+/**
+ * pmcraid_ioa_reset - Implementation of IOA reset logic
+ *
+ * @cmd: pointer to the cmd block to be used for entire reset process
+ *
+ * This function executes most of the steps required for IOA reset. This gets
+ * called by user threads (modprobe/insmod/rmmod) timer, tasklet and midlayer's
+ * 'eh_' thread. Access to variables used for controling the reset sequence is
+ * synchronized using host lock. Various functions called during reset process
+ * would make use of a single command block, pointer to which is also stored in
+ * adapter instance structure.
+ *
+ * Return Value
+ * None
+ */
+static void pmcraid_ioa_reset(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ u8 reset_complete = 0;
+
+ pinstance->ioa_reset_in_progress = 1;
+
+ if (pinstance->reset_cmd != cmd) {
+ pmcraid_err("reset is called with different command block\n");
+ pinstance->reset_cmd = cmd;
+ }
+
+ pmcraid_info("reset_engine: state = %d, command = %p\n",
+ pinstance->ioa_state, cmd);
+
+ switch (pinstance->ioa_state) {
+
+ case IOA_STATE_DEAD:
+ /* If IOA is offline, whatever may be the reset reason, just
+ * return. callers might be waiting on the reset wait_q, wake
+ * up them
+ */
+ pmcraid_err("IOA is offline no reset is possible\n");
+ reset_complete = 1;
+ break;
+
+ case IOA_STATE_IN_BRINGDOWN:
+ /* we enter here, once ioa shutdown command is processed by IOA
+ * Alert IOA for a possible reset. If reset alert fails, IOA
+ * goes through hard-reset
+ */
+ pmcraid_disable_interrupts(pinstance, ~0);
+ pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
+ pmcraid_reset_alert(cmd);
+ break;
+
+ case IOA_STATE_UNKNOWN:
+ /* We may be called during probe or resume. Some pre-processing
+ * is required for prior to reset
+ */
+ scsi_block_requests(pinstance->host);
+
+ /* If asked to reset while IOA was processing responses or
+ * there are any error responses then IOA may require
+ * hard-reset.
+ */
+ if (pinstance->ioa_hard_reset == 0) {
+ if (ioread32(pinstance->ioa_status) &
+ INTRS_TRANSITION_TO_OPERATIONAL) {
+ pmcraid_info("sticky bit set, bring-up\n");
+ pinstance->ioa_state = IOA_STATE_IN_BRINGUP;
+ pmcraid_reinit_cmdblk(cmd);
+ pmcraid_identify_hrrq(cmd);
+ } else {
+ pinstance->ioa_state = IOA_STATE_IN_SOFT_RESET;
+ pmcraid_soft_reset(cmd);
+ }
+ } else {
+ /* Alert IOA of a possible reset and wait for critical
+ * operation in progress bit to reset
+ */
+ pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
+ pmcraid_reset_alert(cmd);
+ }
+ break;
+
+ case IOA_STATE_IN_RESET_ALERT:
+ /* If critical operation in progress bit is reset or wait gets
+ * timed out, reset proceeds with starting BIST on the IOA.
+ * pmcraid_ioa_hard_reset keeps a count of reset attempts. If
+ * they are 3 or more, reset engine marks IOA dead and returns
+ */
+ pinstance->ioa_state = IOA_STATE_IN_HARD_RESET;
+ pmcraid_start_bist(cmd);
+ break;
+
+ case IOA_STATE_IN_HARD_RESET:
+ pinstance->ioa_reset_attempts++;
+
+ /* retry reset if we haven't reached maximum allowed limit */
+ if (pinstance->ioa_reset_attempts > PMCRAID_RESET_ATTEMPTS) {
+ pinstance->ioa_reset_attempts = 0;
+ pmcraid_err("IOA didn't respond marking it as dead\n");
+ pinstance->ioa_state = IOA_STATE_DEAD;
+ reset_complete = 1;
+ break;
+ }
+
+ /* Once either bist or pci reset is done, restore PCI config
+ * space. If this fails, proceed with hard reset again
+ */
+
+ if (pci_restore_state(pinstance->pdev)) {
+ pmcraid_info("config-space error resetting again\n");
+ pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
+ pmcraid_reset_alert(cmd);
+ break;
+ }
+
+ /* fail all pending commands */
+ pmcraid_fail_outstanding_cmds(pinstance);
+
+ /* check if unit check is active, if so extract dump */
+ if (pinstance->ioa_unit_check) {
+ pmcraid_info("unit check is active\n");
+ pinstance->ioa_unit_check = 0;
+ pmcraid_get_dump(pinstance);
+ pinstance->ioa_reset_attempts--;
+ pinstance->ioa_state = IOA_STATE_IN_RESET_ALERT;
+ pmcraid_reset_alert(cmd);
+ break;
+ }
+
+ /* if the reset reason is to bring-down the ioa, we might be
+ * done with the reset restore pci_config_space and complete
+ * the reset
+ */
+ if (pinstance->ioa_bringdown) {
+ pmcraid_info("bringing down the adapter\n");
+ pinstance->ioa_shutdown_type = SHUTDOWN_NONE;
+ pinstance->ioa_bringdown = 0;
+ pinstance->ioa_state = IOA_STATE_UNKNOWN;
+ reset_complete = 1;
+ } else {
+ /* bring-up IOA, so proceed with soft reset
+ * Reinitialize hrrq_buffers and their indices also
+ * enable interrupts after a pci_restore_state
+ */
+ if (pmcraid_reset_enable_ioa(pinstance)) {
+ pinstance->ioa_state = IOA_STATE_IN_BRINGUP;
+ pmcraid_info("bringing up the adapter\n");
+ pmcraid_reinit_cmdblk(cmd);
+ pmcraid_identify_hrrq(cmd);
+ } else {
+ pinstance->ioa_state = IOA_STATE_IN_SOFT_RESET;
+ pmcraid_soft_reset(cmd);
+ }
+ }
+ break;
+
+ case IOA_STATE_IN_SOFT_RESET:
+ /* TRANSITION TO OPERATIONAL is on so start initialization
+ * sequence
+ */
+ pmcraid_info("In softreset proceeding with bring-up\n");
+ pinstance->ioa_state = IOA_STATE_IN_BRINGUP;
+
+ /* Initialization commands start with HRRQ identification. From
+ * now on tasklet completes most of the commands as IOA is up
+ * and intrs are enabled
+ */
+ pmcraid_identify_hrrq(cmd);
+ break;
+
+ case IOA_STATE_IN_BRINGUP:
+ /* we are done with bringing up of IOA, change the ioa_state to
+ * operational and wake up any waiters
+ */
+ pinstance->ioa_state = IOA_STATE_OPERATIONAL;
+ reset_complete = 1;
+ break;
+
+ case IOA_STATE_OPERATIONAL:
+ default:
+ /* When IOA is operational and a reset is requested, check for
+ * the reset reason. If reset is to bring down IOA, unregister
+ * HCAMs and initiate shutdown; if adapter reset is forced then
+ * restart reset sequence again
+ */
+ if (pinstance->ioa_shutdown_type == SHUTDOWN_NONE &&
+ pinstance->force_ioa_reset == 0) {
+ reset_complete = 1;
+ } else {
+ if (pinstance->ioa_shutdown_type != SHUTDOWN_NONE)
+ pinstance->ioa_state = IOA_STATE_IN_BRINGDOWN;
+ pmcraid_reinit_cmdblk(cmd);
+ pmcraid_unregister_hcams(cmd);
+ }
+ break;
+ }
+
+ /* reset will be completed if ioa_state is either DEAD or UNKNOWN or
+ * OPERATIONAL. Reset all control variables used during reset, wake up
+ * any waiting threads and let the SCSI mid-layer send commands. Note
+ * that host_lock must be held before invoking scsi_report_bus_reset.
+ */
+ if (reset_complete) {
+ pinstance->ioa_reset_in_progress = 0;
+ pinstance->ioa_reset_attempts = 0;
+ pinstance->reset_cmd = NULL;
+ pinstance->ioa_shutdown_type = SHUTDOWN_NONE;
+ pinstance->ioa_bringdown = 0;
+ pmcraid_return_cmd(cmd);
+
+ /* If target state is to bring up the adapter, proceed with
+ * hcam registration and resource exposure to mid-layer.
+ */
+ if (pinstance->ioa_state == IOA_STATE_OPERATIONAL)
+ pmcraid_register_hcams(pinstance);
+
+ wake_up_all(&pinstance->reset_wait_q);
+ }
+
+ return;
+}
+
+/**
+ * pmcraid_initiate_reset - initiates reset sequence. This is called from
+ * ISR/tasklet during error interrupts including IOA unit check. If reset
+ * is already in progress, it just returns, otherwise initiates IOA reset
+ * to bring IOA up to operational state.
+ *
+ * @pinstance: pointer to adapter instance structure
+ *
+ * Return value
+ * none
+ */
+static void pmcraid_initiate_reset(struct pmcraid_instance *pinstance)
+{
+ struct pmcraid_cmd *cmd;
+
+ /* If the reset is already in progress, just return, otherwise start
+ * reset sequence and return
+ */
+ if (!pinstance->ioa_reset_in_progress) {
+ scsi_block_requests(pinstance->host);
+ cmd = pmcraid_get_free_cmd(pinstance);
+
+ if (cmd == NULL) {
+ pmcraid_err("no cmnd blocks for initiate_reset\n");
+ return;
+ }
+
+ pinstance->ioa_shutdown_type = SHUTDOWN_NONE;
+ pinstance->reset_cmd = cmd;
+ pinstance->force_ioa_reset = 1;
+ pmcraid_ioa_reset(cmd);
+ }
+}
+
+/**
+ * pmcraid_reset_reload - utility routine for doing IOA reset either to bringup
+ * or bringdown IOA
+ * @pinstance: pointer adapter instance structure
+ * @shutdown_type: shutdown type to be used NONE, NORMAL or ABRREV
+ * @target_state: expected target state after reset
+ *
+ * Note: This command initiates reset and waits for its completion. Hence this
+ * should not be called from isr/timer/tasklet functions (timeout handlers,
+ * error response handlers and interrupt handlers).
+ *
+ * Return Value
+ * 1 in case ioa_state is not target_state, 0 otherwise.
+ */
+static int pmcraid_reset_reload(
+ struct pmcraid_instance *pinstance,
+ u8 shutdown_type,
+ u8 target_state
+)
+{
+ struct pmcraid_cmd *reset_cmd = NULL;
+ unsigned long lock_flags;
+ int reset = 1;
+
+ spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
+
+ if (pinstance->ioa_reset_in_progress) {
+ pmcraid_info("reset_reload: reset is already in progress\n");
+
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+
+ wait_event(pinstance->reset_wait_q,
+ !pinstance->ioa_reset_in_progress);
+
+ spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
+
+ if (pinstance->ioa_state == IOA_STATE_DEAD) {
+ spin_unlock_irqrestore(pinstance->host->host_lock,
+ lock_flags);
+ pmcraid_info("reset_reload: IOA is dead\n");
+ return reset;
+ } else if (pinstance->ioa_state == target_state) {
+ reset = 0;
+ }
+ }
+
+ if (reset) {
+ pmcraid_info("reset_reload: proceeding with reset\n");
+ scsi_block_requests(pinstance->host);
+ reset_cmd = pmcraid_get_free_cmd(pinstance);
+
+ if (reset_cmd == NULL) {
+ pmcraid_err("no free cmnd for reset_reload\n");
+ spin_unlock_irqrestore(pinstance->host->host_lock,
+ lock_flags);
+ return reset;
+ }
+
+ if (shutdown_type == SHUTDOWN_NORMAL)
+ pinstance->ioa_bringdown = 1;
+
+ pinstance->ioa_shutdown_type = shutdown_type;
+ pinstance->reset_cmd = reset_cmd;
+ pinstance->force_ioa_reset = reset;
+ pmcraid_info("reset_reload: initiating reset\n");
+ pmcraid_ioa_reset(reset_cmd);
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+ pmcraid_info("reset_reload: waiting for reset to complete\n");
+ wait_event(pinstance->reset_wait_q,
+ !pinstance->ioa_reset_in_progress);
+
+ pmcraid_info("reset_reload: reset is complete !! \n");
+ scsi_unblock_requests(pinstance->host);
+ if (pinstance->ioa_state == target_state)
+ reset = 0;
+ }
+
+ return reset;
+}
+
+/**
+ * pmcraid_reset_bringdown - wrapper over pmcraid_reset_reload to bringdown IOA
+ *
+ * @pinstance: pointer to adapter instance structure
+ *
+ * Return Value
+ * whatever is returned from pmcraid_reset_reload
+ */
+static int pmcraid_reset_bringdown(struct pmcraid_instance *pinstance)
+{
+ return pmcraid_reset_reload(pinstance,
+ SHUTDOWN_NORMAL,
+ IOA_STATE_UNKNOWN);
+}
+
+/**
+ * pmcraid_reset_bringup - wrapper over pmcraid_reset_reload to bring up IOA
+ *
+ * @pinstance: pointer to adapter instance structure
+ *
+ * Return Value
+ * whatever is returned from pmcraid_reset_reload
+ */
+static int pmcraid_reset_bringup(struct pmcraid_instance *pinstance)
+{
+ return pmcraid_reset_reload(pinstance,
+ SHUTDOWN_NONE,
+ IOA_STATE_OPERATIONAL);
+}
+
+/**
+ * pmcraid_request_sense - Send request sense to a device
+ * @cmd: pmcraid command struct
+ *
+ * This function sends a request sense to a device as a result of a check
+ * condition. This method re-uses the same command block that failed earlier.
+ */
+static void pmcraid_request_sense(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
+ struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
+
+ /* allocate DMAable memory for sense buffers */
+ cmd->sense_buffer = pci_alloc_consistent(cmd->drv_inst->pdev,
+ SCSI_SENSE_BUFFERSIZE,
+ &cmd->sense_buffer_dma);
+
+ if (cmd->sense_buffer == NULL) {
+ pmcraid_err
+ ("couldn't allocate sense buffer for request sense\n");
+ pmcraid_erp_done(cmd);
+ return;
+ }
+
+ /* re-use the command block */
+ memset(&cmd->ioa_cb->ioasa, 0, sizeof(struct pmcraid_ioasa));
+ memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN);
+ ioarcb->request_flags0 = (SYNC_COMPLETE |
+ NO_LINK_DESCS |
+ INHIBIT_UL_CHECK);
+ ioarcb->request_type = REQ_TYPE_SCSI;
+ ioarcb->cdb[0] = REQUEST_SENSE;
+ ioarcb->cdb[4] = SCSI_SENSE_BUFFERSIZE;
+
+ ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) +
+ offsetof(struct pmcraid_ioarcb,
+ add_data.u.ioadl[0]));
+ ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
+
+ ioarcb->data_transfer_length = cpu_to_le32(SCSI_SENSE_BUFFERSIZE);
+
+ ioadl->address = cpu_to_le64(cmd->sense_buffer_dma);
+ ioadl->data_len = cpu_to_le32(SCSI_SENSE_BUFFERSIZE);
+ ioadl->flags = cpu_to_le32(IOADL_FLAGS_LAST_DESC);
+
+ /* request sense might be called as part of error response processing
+ * which runs in tasklets context. It is possible that mid-layer might
+ * schedule queuecommand during this time, hence, writting to IOARRIN
+ * must be protect by host_lock
+ */
+ pmcraid_send_cmd(cmd, pmcraid_erp_done,
+ PMCRAID_REQUEST_SENSE_TIMEOUT,
+ pmcraid_timeout_handler);
+}
+
+/**
+ * pmcraid_cancel_all - cancel all outstanding IOARCBs as part of error recovery
+ * @cmd: command that failed
+ * @sense: true if request_sense is required after cancel all
+ *
+ * This function sends a cancel all to a device to clear the queue.
+ */
+static void pmcraid_cancel_all(struct pmcraid_cmd *cmd, u32 sense)
+{
+ struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
+ struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
+ struct pmcraid_resource_entry *res = scsi_cmd->device->hostdata;
+ void (*cmd_done) (struct pmcraid_cmd *) = sense ? pmcraid_erp_done
+ : pmcraid_request_sense;
+
+ memset(ioarcb->cdb, 0, PMCRAID_MAX_CDB_LEN);
+ ioarcb->request_flags0 = SYNC_OVERRIDE;
+ ioarcb->request_type = REQ_TYPE_IOACMD;
+ ioarcb->cdb[0] = PMCRAID_CANCEL_ALL_REQUESTS;
+
+ if (RES_IS_GSCSI(res->cfg_entry))
+ ioarcb->cdb[1] = PMCRAID_SYNC_COMPLETE_AFTER_CANCEL;
+
+ ioarcb->ioadl_bus_addr = 0;
+ ioarcb->ioadl_length = 0;
+ ioarcb->data_transfer_length = 0;
+ ioarcb->ioarcb_bus_addr &= (~0x1FULL);
+
+ /* writing to IOARRIN must be protected by host_lock, as mid-layer
+ * schedule queuecommand while we are doing this
+ */
+ pmcraid_send_cmd(cmd, cmd_done,
+ PMCRAID_REQUEST_SENSE_TIMEOUT,
+ pmcraid_timeout_handler);
+}
+
+/**
+ * pmcraid_frame_auto_sense: frame fixed format sense information
+ *
+ * @cmd: pointer to failing command block
+ *
+ * Return value
+ * none
+ */
+static void pmcraid_frame_auto_sense(struct pmcraid_cmd *cmd)
+{
+ u8 *sense_buf = cmd->scsi_cmd->sense_buffer;
+ struct pmcraid_resource_entry *res = cmd->scsi_cmd->device->hostdata;
+ struct pmcraid_ioasa *ioasa = &cmd->ioa_cb->ioasa;
+ u32 ioasc = le32_to_cpu(ioasa->ioasc);
+ u32 failing_lba = 0;
+
+ memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE);
+ cmd->scsi_cmd->result = SAM_STAT_CHECK_CONDITION;
+
+ if (RES_IS_VSET(res->cfg_entry) &&
+ ioasc == PMCRAID_IOASC_ME_READ_ERROR_NO_REALLOC &&
+ ioasa->u.vset.failing_lba_hi != 0) {
+
+ sense_buf[0] = 0x72;
+ sense_buf[1] = PMCRAID_IOASC_SENSE_KEY(ioasc);
+ sense_buf[2] = PMCRAID_IOASC_SENSE_CODE(ioasc);
+ sense_buf[3] = PMCRAID_IOASC_SENSE_QUAL(ioasc);
+
+ sense_buf[7] = 12;
+ sense_buf[8] = 0;
+ sense_buf[9] = 0x0A;
+ sense_buf[10] = 0x80;
+
+ failing_lba = le32_to_cpu(ioasa->u.vset.failing_lba_hi);
+
+ sense_buf[12] = (failing_lba & 0xff000000) >> 24;
+ sense_buf[13] = (failing_lba & 0x00ff0000) >> 16;
+ sense_buf[14] = (failing_lba & 0x0000ff00) >> 8;
+ sense_buf[15] = failing_lba & 0x000000ff;
+
+ failing_lba = le32_to_cpu(ioasa->u.vset.failing_lba_lo);
+
+ sense_buf[16] = (failing_lba & 0xff000000) >> 24;
+ sense_buf[17] = (failing_lba & 0x00ff0000) >> 16;
+ sense_buf[18] = (failing_lba & 0x0000ff00) >> 8;
+ sense_buf[19] = failing_lba & 0x000000ff;
+ } else {
+ sense_buf[0] = 0x70;
+ sense_buf[2] = PMCRAID_IOASC_SENSE_KEY(ioasc);
+ sense_buf[12] = PMCRAID_IOASC_SENSE_CODE(ioasc);
+ sense_buf[13] = PMCRAID_IOASC_SENSE_QUAL(ioasc);
+
+ if (ioasc == PMCRAID_IOASC_ME_READ_ERROR_NO_REALLOC) {
+ if (RES_IS_VSET(res->cfg_entry))
+ failing_lba =
+ le32_to_cpu(ioasa->u.
+ vset.failing_lba_lo);
+ sense_buf[0] |= 0x80;
+ sense_buf[3] = (failing_lba >> 24) & 0xff;
+ sense_buf[4] = (failing_lba >> 16) & 0xff;
+ sense_buf[5] = (failing_lba >> 8) & 0xff;
+ sense_buf[6] = failing_lba & 0xff;
+ }
+
+ sense_buf[7] = 6; /* additional length */
+ }
+}
+
+/**
+ * pmcraid_error_handler - Error response handlers for a SCSI op
+ * @cmd: pointer to pmcraid_cmd that has failed
+ *
+ * This function determines whether or not to initiate ERP on the affected
+ * device. This is called from a tasklet, which doesn't hold any locks.
+ *
+ * Return value:
+ * 0 it caller can complete the request, otherwise 1 where in error
+ * handler itself completes the request and returns the command block
+ * back to free-pool
+ */
+static int pmcraid_error_handler(struct pmcraid_cmd *cmd)
+{
+ struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
+ struct pmcraid_resource_entry *res = scsi_cmd->device->hostdata;
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ struct pmcraid_ioasa *ioasa = &cmd->ioa_cb->ioasa;
+ u32 ioasc = le32_to_cpu(ioasa->ioasc);
+ u32 masked_ioasc = ioasc & PMCRAID_IOASC_SENSE_MASK;
+ u32 sense_copied = 0;
+
+ if (!res) {
+ pmcraid_info("resource pointer is NULL\n");
+ return 0;
+ }
+
+ /* If this was a SCSI read/write command keep count of errors */
+ if (SCSI_CMD_TYPE(scsi_cmd->cmnd[0]) == SCSI_READ_CMD)
+ atomic_inc(&res->read_failures);
+ else if (SCSI_CMD_TYPE(scsi_cmd->cmnd[0]) == SCSI_WRITE_CMD)
+ atomic_inc(&res->write_failures);
+
+ if (!RES_IS_GSCSI(res->cfg_entry) &&
+ masked_ioasc != PMCRAID_IOASC_HW_DEVICE_BUS_STATUS_ERROR) {
+ pmcraid_frame_auto_sense(cmd);
+ }
+
+ /* Log IOASC/IOASA information based on user settings */
+ pmcraid_ioasc_logger(ioasc, cmd);
+
+ switch (masked_ioasc) {
+
+ case PMCRAID_IOASC_AC_TERMINATED_BY_HOST:
+ scsi_cmd->result |= (DID_ABORT << 16);
+ break;
+
+ case PMCRAID_IOASC_IR_INVALID_RESOURCE_HANDLE:
+ case PMCRAID_IOASC_HW_CANNOT_COMMUNICATE:
+ scsi_cmd->result |= (DID_NO_CONNECT << 16);
+ break;
+
+ case PMCRAID_IOASC_NR_SYNC_REQUIRED:
+ res->sync_reqd = 1;
+ scsi_cmd->result |= (DID_IMM_RETRY << 16);
+ break;
+
+ case PMCRAID_IOASC_ME_READ_ERROR_NO_REALLOC:
+ scsi_cmd->result |= (DID_PASSTHROUGH << 16);
+ break;
+
+ case PMCRAID_IOASC_UA_BUS_WAS_RESET:
+ case PMCRAID_IOASC_UA_BUS_WAS_RESET_BY_OTHER:
+ if (!res->reset_progress)
+ scsi_report_bus_reset(pinstance->host,
+ scsi_cmd->device->channel);
+ scsi_cmd->result |= (DID_ERROR << 16);
+ break;
+
+ case PMCRAID_IOASC_HW_DEVICE_BUS_STATUS_ERROR:
+ scsi_cmd->result |= PMCRAID_IOASC_SENSE_STATUS(ioasc);
+ res->sync_reqd = 1;
+
+ /* if check_condition is not active return with error otherwise
+ * get/frame the sense buffer
+ */
+ if (PMCRAID_IOASC_SENSE_STATUS(ioasc) !=
+ SAM_STAT_CHECK_CONDITION &&
+ PMCRAID_IOASC_SENSE_STATUS(ioasc) != SAM_STAT_ACA_ACTIVE)
+ return 0;
+
+ /* If we have auto sense data as part of IOASA pass it to
+ * mid-layer
+ */
+ if (ioasa->auto_sense_length != 0) {
+ short sense_len = ioasa->auto_sense_length;
+ int data_size = min_t(u16, le16_to_cpu(sense_len),
+ SCSI_SENSE_BUFFERSIZE);
+
+ memcpy(scsi_cmd->sense_buffer,
+ ioasa->sense_data,
+ data_size);
+ sense_copied = 1;
+ }
+
+ if (RES_IS_GSCSI(res->cfg_entry)) {
+ pmcraid_cancel_all(cmd, sense_copied);
+ } else if (sense_copied) {
+ pmcraid_erp_done(cmd);
+ return 0;
+ } else {
+ pmcraid_request_sense(cmd);
+ }
+
+ return 1;
+
+ case PMCRAID_IOASC_NR_INIT_CMD_REQUIRED:
+ break;
+
+ default:
+ if (PMCRAID_IOASC_SENSE_KEY(ioasc) > RECOVERED_ERROR)
+ scsi_cmd->result |= (DID_ERROR << 16);
+ break;
+ }
+ return 0;
+}
+
+/**
+ * pmcraid_reset_device - device reset handler functions
+ *
+ * @scsi_cmd: scsi command struct
+ * @modifier: reset modifier indicating the reset sequence to be performed
+ *
+ * This function issues a device reset to the affected device.
+ * A LUN reset will be sent to the device first. If that does
+ * not work, a target reset will be sent.
+ *
+ * Return value:
+ * SUCCESS / FAILED
+ */
+static int pmcraid_reset_device(
+ struct scsi_cmnd *scsi_cmd,
+ unsigned long timeout,
+ u8 modifier
+)
+{
+ struct pmcraid_cmd *cmd;
+ struct pmcraid_instance *pinstance;
+ struct pmcraid_resource_entry *res;
+ struct pmcraid_ioarcb *ioarcb;
+ unsigned long lock_flags;
+ u32 ioasc;
+
+ pinstance =
+ (struct pmcraid_instance *)scsi_cmd->device->host->hostdata;
+ res = scsi_cmd->device->hostdata;
+
+ if (!res) {
+ pmcraid_err("reset_device: NULL resource pointer\n");
+ return FAILED;
+ }
+
+ /* If adapter is currently going through reset/reload, return failed.
+ * This will force the mid-layer to call _eh_bus/host reset, which
+ * will then go to sleep and wait for the reset to complete
+ */
+ spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
+ if (pinstance->ioa_reset_in_progress ||
+ pinstance->ioa_state == IOA_STATE_DEAD) {
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+ return FAILED;
+ }
+
+ res->reset_progress = 1;
+ pmcraid_info("Resetting %s resource with addr %x\n",
+ ((modifier & RESET_DEVICE_LUN) ? "LUN" :
+ ((modifier & RESET_DEVICE_TARGET) ? "TARGET" : "BUS")),
+ le32_to_cpu(res->cfg_entry.resource_address));
+
+ /* get a free cmd block */
+ cmd = pmcraid_get_free_cmd(pinstance);
+
+ if (cmd == NULL) {
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+ pmcraid_err("%s: no cmd blocks are available\n", __func__);
+ return FAILED;
+ }
+
+ ioarcb = &cmd->ioa_cb->ioarcb;
+ ioarcb->resource_handle = res->cfg_entry.resource_handle;
+ ioarcb->request_type = REQ_TYPE_IOACMD;
+ ioarcb->cdb[0] = PMCRAID_RESET_DEVICE;
+
+ /* Initialize reset modifier bits */
+ if (modifier)
+ modifier = ENABLE_RESET_MODIFIER | modifier;
+
+ ioarcb->cdb[1] = modifier;
+
+ init_completion(&cmd->wait_for_completion);
+ cmd->completion_req = 1;
+
+ pmcraid_info("cmd(CDB[0] = %x) for %x with index = %d\n",
+ cmd->ioa_cb->ioarcb.cdb[0],
+ le32_to_cpu(cmd->ioa_cb->ioarcb.resource_handle),
+ le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2);
+
+ pmcraid_send_cmd(cmd,
+ pmcraid_internal_done,
+ timeout,
+ pmcraid_timeout_handler);
+
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+
+ /* RESET_DEVICE command completes after all pending IOARCBs are
+ * completed. Once this command is completed, pmcraind_internal_done
+ * will wake up the 'completion' queue.
+ */
+ wait_for_completion(&cmd->wait_for_completion);
+
+ /* complete the command here itself and return the command block
+ * to free list
+ */
+ pmcraid_return_cmd(cmd);
+ res->reset_progress = 0;
+ ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
+
+ /* set the return value based on the returned ioasc */
+ return PMCRAID_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS;
+}
+
+/**
+ * _pmcraid_io_done - helper for pmcraid_io_done function
+ *
+ * @cmd: pointer to pmcraid command struct
+ * @reslen: residual data length to be set in the ioasa
+ * @ioasc: ioasc either returned by IOA or set by driver itself.
+ *
+ * This function is invoked by pmcraid_io_done to complete mid-layer
+ * scsi ops.
+ *
+ * Return value:
+ * 0 if caller is required to return it to free_pool. Returns 1 if
+ * caller need not worry about freeing command block as error handler
+ * will take care of that.
+ */
+
+static int _pmcraid_io_done(struct pmcraid_cmd *cmd, int reslen, int ioasc)
+{
+ struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
+ int rc = 0;
+
+ scsi_set_resid(scsi_cmd, reslen);
+
+ pmcraid_info("response(%d) CDB[0] = %x ioasc:result: %x:%x\n",
+ le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2,
+ cmd->ioa_cb->ioarcb.cdb[0],
+ ioasc, scsi_cmd->result);
+
+ if (PMCRAID_IOASC_SENSE_KEY(ioasc) != 0)
+ rc = pmcraid_error_handler(cmd);
+
+ if (rc == 0) {
+ scsi_dma_unmap(scsi_cmd);
+ scsi_cmd->scsi_done(scsi_cmd);
+ }
+
+ return rc;
+}
+
+/**
+ * pmcraid_io_done - SCSI completion function
+ *
+ * @cmd: pointer to pmcraid command struct
+ *
+ * This function is invoked by tasklet/mid-layer error handler to completing
+ * the SCSI ops sent from mid-layer.
+ *
+ * Return value
+ * none
+ */
+
+static void pmcraid_io_done(struct pmcraid_cmd *cmd)
+{
+ u32 ioasc = le32_to_cpu(cmd->ioa_cb->ioasa.ioasc);
+ u32 reslen = le32_to_cpu(cmd->ioa_cb->ioasa.residual_data_length);
+
+ if (_pmcraid_io_done(cmd, reslen, ioasc) == 0)
+ pmcraid_return_cmd(cmd);
+}
+
+/**
+ * pmcraid_abort_cmd - Aborts a single IOARCB already submitted to IOA
+ *
+ * @cmd: command block of the command to be aborted
+ *
+ * Return Value:
+ * returns pointer to command structure used as cancelling cmd
+ */
+static struct pmcraid_cmd *pmcraid_abort_cmd(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_cmd *cancel_cmd;
+ struct pmcraid_instance *pinstance;
+ struct pmcraid_resource_entry *res;
+
+ pinstance = (struct pmcraid_instance *)cmd->drv_inst;
+ res = cmd->scsi_cmd->device->hostdata;
+
+ cancel_cmd = pmcraid_get_free_cmd(pinstance);
+
+ if (cancel_cmd == NULL) {
+ pmcraid_err("%s: no cmd blocks are available\n", __func__);
+ return NULL;
+ }
+
+ pmcraid_prepare_cancel_cmd(cancel_cmd, cmd);
+
+ pmcraid_info("aborting command CDB[0]= %x with index = %d\n",
+ cmd->ioa_cb->ioarcb.cdb[0],
+ cmd->ioa_cb->ioarcb.response_handle >> 2);
+
+ init_completion(&cancel_cmd->wait_for_completion);
+ cancel_cmd->completion_req = 1;
+
+ pmcraid_info("command (%d) CDB[0] = %x for %x\n",
+ le32_to_cpu(cancel_cmd->ioa_cb->ioarcb.response_handle) >> 2,
+ cmd->ioa_cb->ioarcb.cdb[0],
+ le32_to_cpu(cancel_cmd->ioa_cb->ioarcb.resource_handle));
+
+ pmcraid_send_cmd(cancel_cmd,
+ pmcraid_internal_done,
+ PMCRAID_INTERNAL_TIMEOUT,
+ pmcraid_timeout_handler);
+ return cancel_cmd;
+}
+
+/**
+ * pmcraid_abort_complete - Waits for ABORT TASK completion
+ *
+ * @cancel_cmd: command block use as cancelling command
+ *
+ * Return Value:
+ * returns SUCCESS if ABORT TASK has good completion
+ * otherwise FAILED
+ */
+static int pmcraid_abort_complete(struct pmcraid_cmd *cancel_cmd)
+{
+ struct pmcraid_resource_entry *res;
+ u32 ioasc;
+
+ wait_for_completion(&cancel_cmd->wait_for_completion);
+ res = cancel_cmd->u.res;
+ cancel_cmd->u.res = NULL;
+ ioasc = le32_to_cpu(cancel_cmd->ioa_cb->ioasa.ioasc);
+
+ /* If the abort task is not timed out we will get a Good completion
+ * as sense_key, otherwise we may get one the following responses
+ * due to subsquent bus reset or device reset. In case IOASC is
+ * NR_SYNC_REQUIRED, set sync_reqd flag for the corresponding resource
+ */
+ if (ioasc == PMCRAID_IOASC_UA_BUS_WAS_RESET ||
+ ioasc == PMCRAID_IOASC_NR_SYNC_REQUIRED) {
+ if (ioasc == PMCRAID_IOASC_NR_SYNC_REQUIRED)
+ res->sync_reqd = 1;
+ ioasc = 0;
+ }
+
+ /* complete the command here itself */
+ pmcraid_return_cmd(cancel_cmd);
+ return PMCRAID_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS;
+}
+
+/**
+ * pmcraid_eh_abort_handler - entry point for aborting a single task on errors
+ *
+ * @scsi_cmd: scsi command struct given by mid-layer. When this is called
+ * mid-layer ensures that no other commands are queued. This
+ * never gets called under interrupt, but a separate eh thread.
+ *
+ * Return value:
+ * SUCCESS / FAILED
+ */
+static int pmcraid_eh_abort_handler(struct scsi_cmnd *scsi_cmd)
+{
+ struct pmcraid_instance *pinstance;
+ struct pmcraid_cmd *cmd;
+ struct pmcraid_resource_entry *res;
+ unsigned long host_lock_flags;
+ unsigned long pending_lock_flags;
+ struct pmcraid_cmd *cancel_cmd = NULL;
+ int cmd_found = 0;
+ int rc = FAILED;
+
+ pinstance =
+ (struct pmcraid_instance *)scsi_cmd->device->host->hostdata;
+
+ dev_err(&pinstance->pdev->dev,
+ "I/O command timed out, aborting it.\n");
+
+ res = scsi_cmd->device->hostdata;
+
+ if (res == NULL)
+ return rc;
+
+ /* If we are currently going through reset/reload, return failed.
+ * This will force the mid-layer to eventually call
+ * pmcraid_eh_host_reset which will then go to sleep and wait for the
+ * reset to complete
+ */
+ spin_lock_irqsave(pinstance->host->host_lock, host_lock_flags);
+
+ if (pinstance->ioa_reset_in_progress ||
+ pinstance->ioa_state == IOA_STATE_DEAD) {
+ spin_unlock_irqrestore(pinstance->host->host_lock,
+ host_lock_flags);
+ return rc;
+ }
+
+ /* loop over pending cmd list to find cmd corresponding to this
+ * scsi_cmd. Note that this command might not have been completed
+ * already. locking: all pending commands are protected with
+ * pending_pool_lock.
+ */
+ spin_lock_irqsave(&pinstance->pending_pool_lock, pending_lock_flags);
+ list_for_each_entry(cmd, &pinstance->pending_cmd_pool, free_list) {
+
+ if (cmd->scsi_cmd == scsi_cmd) {
+ cmd_found = 1;
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&pinstance->pending_pool_lock,
+ pending_lock_flags);
+
+ /* If the command to be aborted was given to IOA and still pending with
+ * it, send ABORT_TASK to abort this and wait for its completion
+ */
+ if (cmd_found)
+ cancel_cmd = pmcraid_abort_cmd(cmd);
+
+ spin_unlock_irqrestore(pinstance->host->host_lock,
+ host_lock_flags);
+
+ if (cancel_cmd) {
+ cancel_cmd->u.res = cmd->scsi_cmd->device->hostdata;
+ rc = pmcraid_abort_complete(cancel_cmd);
+ }
+
+ return cmd_found ? rc : SUCCESS;
+}
+
+/**
+ * pmcraid_eh_xxxx_reset_handler - bus/target/device reset handler callbacks
+ *
+ * @scmd: pointer to scsi_cmd that was sent to the resource to be reset.
+ *
+ * All these routines invokve pmcraid_reset_device with appropriate parameters.
+ * Since these are called from mid-layer EH thread, no other IO will be queued
+ * to the resource being reset. However, control path (IOCTL) may be active so
+ * it is necessary to synchronize IOARRIN writes which pmcraid_reset_device
+ * takes care by locking/unlocking host_lock.
+ *
+ * Return value
+ * SUCCESS or FAILED
+ */
+static int pmcraid_eh_device_reset_handler(struct scsi_cmnd *scmd)
+{
+ pmcraid_err("Doing device reset due to an I/O command timeout.\n");
+ return pmcraid_reset_device(scmd,
+ PMCRAID_INTERNAL_TIMEOUT,
+ RESET_DEVICE_LUN);
+}
+
+static int pmcraid_eh_bus_reset_handler(struct scsi_cmnd *scmd)
+{
+ pmcraid_err("Doing bus reset due to an I/O command timeout.\n");
+ return pmcraid_reset_device(scmd,
+ PMCRAID_RESET_BUS_TIMEOUT,
+ RESET_DEVICE_BUS);
+}
+
+static int pmcraid_eh_target_reset_handler(struct scsi_cmnd *scmd)
+{
+ pmcraid_err("Doing target reset due to an I/O command timeout.\n");
+ return pmcraid_reset_device(scmd,
+ PMCRAID_INTERNAL_TIMEOUT,
+ RESET_DEVICE_TARGET);
+}
+
+/**
+ * pmcraid_eh_host_reset_handler - adapter reset handler callback
+ *
+ * @scmd: pointer to scsi_cmd that was sent to a resource of adapter
+ *
+ * Initiates adapter reset to bring it up to operational state
+ *
+ * Return value
+ * SUCCESS or FAILED
+ */
+static int pmcraid_eh_host_reset_handler(struct scsi_cmnd *scmd)
+{
+ unsigned long interval = 10000; /* 10 seconds interval */
+ int waits = jiffies_to_msecs(PMCRAID_RESET_HOST_TIMEOUT) / interval;
+ struct pmcraid_instance *pinstance =
+ (struct pmcraid_instance *)(scmd->device->host->hostdata);
+
+
+ /* wait for an additional 150 seconds just in case firmware could come
+ * up and if it could complete all the pending commands excluding the
+ * two HCAM (CCN and LDN).
+ */
+ while (waits--) {
+ if (atomic_read(&pinstance->outstanding_cmds) <=
+ PMCRAID_MAX_HCAM_CMD)
+ return SUCCESS;
+ msleep(interval);
+ }
+
+ dev_err(&pinstance->pdev->dev,
+ "Adapter being reset due to an I/O command timeout.\n");
+ return pmcraid_reset_bringup(pinstance) == 0 ? SUCCESS : FAILED;
+}
+
+/**
+ * pmcraid_task_attributes - Translate SPI Q-Tags to task attributes
+ * @scsi_cmd: scsi command struct
+ *
+ * Return value
+ * number of tags or 0 if the task is not tagged
+ */
+static u8 pmcraid_task_attributes(struct scsi_cmnd *scsi_cmd)
+{
+ char tag[2];
+ u8 rc = 0;
+
+ if (scsi_populate_tag_msg(scsi_cmd, tag)) {
+ switch (tag[0]) {
+ case MSG_SIMPLE_TAG:
+ rc = TASK_TAG_SIMPLE;
+ break;
+ case MSG_HEAD_TAG:
+ rc = TASK_TAG_QUEUE_HEAD;
+ break;
+ case MSG_ORDERED_TAG:
+ rc = TASK_TAG_ORDERED;
+ break;
+ };
+ }
+
+ return rc;
+}
+
+
+/**
+ * pmcraid_init_ioadls - initializes IOADL related fields in IOARCB
+ * @cmd: pmcraid command struct
+ * @sgcount: count of scatter-gather elements
+ *
+ * Return value
+ * returns pointer pmcraid_ioadl_desc, initialized to point to internal
+ * or external IOADLs
+ */
+struct pmcraid_ioadl_desc *
+pmcraid_init_ioadls(struct pmcraid_cmd *cmd, int sgcount)
+{
+ struct pmcraid_ioadl_desc *ioadl;
+ struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
+ int ioadl_count = 0;
+
+ if (ioarcb->add_cmd_param_length)
+ ioadl_count = DIV_ROUND_UP(ioarcb->add_cmd_param_length, 16);
+ ioarcb->ioadl_length =
+ sizeof(struct pmcraid_ioadl_desc) * sgcount;
+
+ if ((sgcount + ioadl_count) > (ARRAY_SIZE(ioarcb->add_data.u.ioadl))) {
+ /* external ioadls start at offset 0x80 from control_block
+ * structure, re-using 24 out of 27 ioadls part of IOARCB.
+ * It is necessary to indicate to firmware that driver is
+ * using ioadls to be treated as external to IOARCB.
+ */
+ ioarcb->ioarcb_bus_addr &= ~(0x1FULL);
+ ioarcb->ioadl_bus_addr =
+ cpu_to_le64((cmd->ioa_cb_bus_addr) +
+ offsetof(struct pmcraid_ioarcb,
+ add_data.u.ioadl[3]));
+ ioadl = &ioarcb->add_data.u.ioadl[3];
+ } else {
+ ioarcb->ioadl_bus_addr =
+ cpu_to_le64((cmd->ioa_cb_bus_addr) +
+ offsetof(struct pmcraid_ioarcb,
+ add_data.u.ioadl[ioadl_count]));
+
+ ioadl = &ioarcb->add_data.u.ioadl[ioadl_count];
+ ioarcb->ioarcb_bus_addr |=
+ DIV_ROUND_CLOSEST(sgcount + ioadl_count, 8);
+ }
+
+ return ioadl;
+}
+
+/**
+ * pmcraid_build_ioadl - Build a scatter/gather list and map the buffer
+ * @pinstance: pointer to adapter instance structure
+ * @cmd: pmcraid command struct
+ *
+ * This function is invoked by queuecommand entry point while sending a command
+ * to firmware. This builds ioadl descriptors and sets up ioarcb fields.
+ *
+ * Return value:
+ * 0 on success or -1 on failure
+ */
+static int pmcraid_build_ioadl(
+ struct pmcraid_instance *pinstance,
+ struct pmcraid_cmd *cmd
+)
+{
+ int i, nseg;
+ struct scatterlist *sglist;
+
+ struct scsi_cmnd *scsi_cmd = cmd->scsi_cmd;
+ struct pmcraid_ioarcb *ioarcb = &(cmd->ioa_cb->ioarcb);
+ struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
+
+ u32 length = scsi_bufflen(scsi_cmd);
+
+ if (!length)
+ return 0;
+
+ nseg = scsi_dma_map(scsi_cmd);
+
+ if (nseg < 0) {
+ dev_err(&pinstance->pdev->dev, "scsi_map_dma failed!\n");
+ return -1;
+ } else if (nseg > PMCRAID_MAX_IOADLS) {
+ scsi_dma_unmap(scsi_cmd);
+ dev_err(&pinstance->pdev->dev,
+ "sg count is (%d) more than allowed!\n", nseg);
+ return -1;
+ }
+
+ /* Initialize IOARCB data transfer length fields */
+ if (scsi_cmd->sc_data_direction == DMA_TO_DEVICE)
+ ioarcb->request_flags0 |= TRANSFER_DIR_WRITE;
+
+ ioarcb->request_flags0 |= NO_LINK_DESCS;
+ ioarcb->data_transfer_length = cpu_to_le32(length);
+ ioadl = pmcraid_init_ioadls(cmd, nseg);
+
+ /* Initialize IOADL descriptor addresses */
+ scsi_for_each_sg(scsi_cmd, sglist, nseg, i) {
+ ioadl[i].data_len = cpu_to_le32(sg_dma_len(sglist));
+ ioadl[i].address = cpu_to_le64(sg_dma_address(sglist));
+ ioadl[i].flags = 0;
+ }
+ /* setup last descriptor */
+ ioadl[i - 1].flags = cpu_to_le32(IOADL_FLAGS_LAST_DESC);
+
+ return 0;
+}
+
+/**
+ * pmcraid_free_sglist - Frees an allocated SG buffer list
+ * @sglist: scatter/gather list pointer
+ *
+ * Free a DMA'able memory previously allocated with pmcraid_alloc_sglist
+ *
+ * Return value:
+ * none
+ */
+static void pmcraid_free_sglist(struct pmcraid_sglist *sglist)
+{
+ int i;
+
+ for (i = 0; i < sglist->num_sg; i++)
+ __free_pages(sg_page(&(sglist->scatterlist[i])),
+ sglist->order);
+
+ kfree(sglist);
+}
+
+/**
+ * pmcraid_alloc_sglist - Allocates memory for a SG list
+ * @buflen: buffer length
+ *
+ * Allocates a DMA'able buffer in chunks and assembles a scatter/gather
+ * list.
+ *
+ * Return value
+ * pointer to sglist / NULL on failure
+ */
+static struct pmcraid_sglist *pmcraid_alloc_sglist(int buflen)
+{
+ struct pmcraid_sglist *sglist;
+ struct scatterlist *scatterlist;
+ struct page *page;
+ int num_elem, i, j;
+ int sg_size;
+ int order;
+ int bsize_elem;
+
+ sg_size = buflen / (PMCRAID_MAX_IOADLS - 1);
+ order = (sg_size > 0) ? get_order(sg_size) : 0;
+ bsize_elem = PAGE_SIZE * (1 << order);
+
+ /* Determine the actual number of sg entries needed */
+ if (buflen % bsize_elem)
+ num_elem = (buflen / bsize_elem) + 1;
+ else
+ num_elem = buflen / bsize_elem;
+
+ /* Allocate a scatter/gather list for the DMA */
+ sglist = kzalloc(sizeof(struct pmcraid_sglist) +
+ (sizeof(struct scatterlist) * (num_elem - 1)),
+ GFP_KERNEL);
+
+ if (sglist == NULL)
+ return NULL;
+
+ scatterlist = sglist->scatterlist;
+ sg_init_table(scatterlist, num_elem);
+ sglist->order = order;
+ sglist->num_sg = num_elem;
+ sg_size = buflen;
+
+ for (i = 0; i < num_elem; i++) {
+ page = alloc_pages(GFP_KERNEL|GFP_DMA, order);
+ if (!page) {
+ for (j = i - 1; j >= 0; j--)
+ __free_pages(sg_page(&scatterlist[j]), order);
+ kfree(sglist);
+ return NULL;
+ }
+
+ sg_set_page(&scatterlist[i], page,
+ sg_size < bsize_elem ? sg_size : bsize_elem, 0);
+ sg_size -= bsize_elem;
+ }
+
+ return sglist;
+}
+
+/**
+ * pmcraid_copy_sglist - Copy user buffer to kernel buffer's SG list
+ * @sglist: scatter/gather list pointer
+ * @buffer: buffer pointer
+ * @len: buffer length
+ * @direction: data transfer direction
+ *
+ * Copy a user buffer into a buffer allocated by pmcraid_alloc_sglist
+ *
+ * Return value:
+ * 0 on success / other on failure
+ */
+static int pmcraid_copy_sglist(
+ struct pmcraid_sglist *sglist,
+ unsigned long buffer,
+ u32 len,
+ int direction
+)
+{
+ struct scatterlist *scatterlist;
+ void *kaddr;
+ int bsize_elem;
+ int i;
+ int rc = 0;
+
+ /* Determine the actual number of bytes per element */
+ bsize_elem = PAGE_SIZE * (1 << sglist->order);
+
+ scatterlist = sglist->scatterlist;
+
+ for (i = 0; i < (len / bsize_elem); i++, buffer += bsize_elem) {
+ struct page *page = sg_page(&scatterlist[i]);
+
+ kaddr = kmap(page);
+ if (direction == DMA_TO_DEVICE)
+ rc = __copy_from_user(kaddr,
+ (void *)buffer,
+ bsize_elem);
+ else
+ rc = __copy_to_user((void *)buffer, kaddr, bsize_elem);
+
+ kunmap(page);
+
+ if (rc) {
+ pmcraid_err("failed to copy user data into sg list\n");
+ return -EFAULT;
+ }
+
+ scatterlist[i].length = bsize_elem;
+ }
+
+ if (len % bsize_elem) {
+ struct page *page = sg_page(&scatterlist[i]);
+
+ kaddr = kmap(page);
+
+ if (direction == DMA_TO_DEVICE)
+ rc = __copy_from_user(kaddr,
+ (void *)buffer,
+ len % bsize_elem);
+ else
+ rc = __copy_to_user((void *)buffer,
+ kaddr,
+ len % bsize_elem);
+
+ kunmap(page);
+
+ scatterlist[i].length = len % bsize_elem;
+ }
+
+ if (rc) {
+ pmcraid_err("failed to copy user data into sg list\n");
+ rc = -EFAULT;
+ }
+
+ return rc;
+}
+
+/**
+ * pmcraid_queuecommand - Queue a mid-layer request
+ * @scsi_cmd: scsi command struct
+ * @done: done function
+ *
+ * This function queues a request generated by the mid-layer. Midlayer calls
+ * this routine within host->lock. Some of the functions called by queuecommand
+ * would use cmd block queue locks (free_pool_lock and pending_pool_lock)
+ *
+ * Return value:
+ * 0 on success
+ * SCSI_MLQUEUE_DEVICE_BUSY if device is busy
+ * SCSI_MLQUEUE_HOST_BUSY if host is busy
+ */
+static int pmcraid_queuecommand(
+ struct scsi_cmnd *scsi_cmd,
+ void (*done) (struct scsi_cmnd *)
+)
+{
+ struct pmcraid_instance *pinstance;
+ struct pmcraid_resource_entry *res;
+ struct pmcraid_ioarcb *ioarcb;
+ struct pmcraid_cmd *cmd;
+ int rc = 0;
+
+ pinstance =
+ (struct pmcraid_instance *)scsi_cmd->device->host->hostdata;
+
+ scsi_cmd->scsi_done = done;
+ res = scsi_cmd->device->hostdata;
+ scsi_cmd->result = (DID_OK << 16);
+
+ /* if adapter is marked as dead, set result to DID_NO_CONNECT complete
+ * the command
+ */
+ if (pinstance->ioa_state == IOA_STATE_DEAD) {
+ pmcraid_info("IOA is dead, but queuecommand is scheduled\n");
+ scsi_cmd->result = (DID_NO_CONNECT << 16);
+ scsi_cmd->scsi_done(scsi_cmd);
+ return 0;
+ }
+
+ /* If IOA reset is in progress, can't queue the commands */
+ if (pinstance->ioa_reset_in_progress)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ /* initialize the command and IOARCB to be sent to IOA */
+ cmd = pmcraid_get_free_cmd(pinstance);
+
+ if (cmd == NULL) {
+ pmcraid_err("free command block is not available\n");
+ return SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ cmd->scsi_cmd = scsi_cmd;
+ ioarcb = &(cmd->ioa_cb->ioarcb);
+ memcpy(ioarcb->cdb, scsi_cmd->cmnd, scsi_cmd->cmd_len);
+ ioarcb->resource_handle = res->cfg_entry.resource_handle;
+ ioarcb->request_type = REQ_TYPE_SCSI;
+
+ cmd->cmd_done = pmcraid_io_done;
+
+ if (RES_IS_GSCSI(res->cfg_entry) || RES_IS_VSET(res->cfg_entry)) {
+ if (scsi_cmd->underflow == 0)
+ ioarcb->request_flags0 |= INHIBIT_UL_CHECK;
+
+ if (res->sync_reqd) {
+ ioarcb->request_flags0 |= SYNC_COMPLETE;
+ res->sync_reqd = 0;
+ }
+
+ ioarcb->request_flags0 |= NO_LINK_DESCS;
+ ioarcb->request_flags1 |= pmcraid_task_attributes(scsi_cmd);
+
+ if (RES_IS_GSCSI(res->cfg_entry))
+ ioarcb->request_flags1 |= DELAY_AFTER_RESET;
+ }
+
+ rc = pmcraid_build_ioadl(pinstance, cmd);
+
+ pmcraid_info("command (%d) CDB[0] = %x for %x:%x:%x:%x\n",
+ le32_to_cpu(ioarcb->response_handle) >> 2,
+ scsi_cmd->cmnd[0], pinstance->host->unique_id,
+ RES_IS_VSET(res->cfg_entry) ? PMCRAID_VSET_BUS_ID :
+ PMCRAID_PHYS_BUS_ID,
+ RES_IS_VSET(res->cfg_entry) ?
+ res->cfg_entry.unique_flags1 :
+ RES_TARGET(res->cfg_entry.resource_address),
+ RES_LUN(res->cfg_entry.resource_address));
+
+ if (likely(rc == 0)) {
+ _pmcraid_fire_command(cmd);
+ } else {
+ pmcraid_err("queuecommand could not build ioadl\n");
+ pmcraid_return_cmd(cmd);
+ rc = SCSI_MLQUEUE_HOST_BUSY;
+ }
+
+ return rc;
+}
+
+/**
+ * pmcraid_open -char node "open" entry, allowed only users with admin access
+ */
+static int pmcraid_chr_open(struct inode *inode, struct file *filep)
+{
+ struct pmcraid_instance *pinstance;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ /* Populate adapter instance * pointer for use by ioctl */
+ pinstance = container_of(inode->i_cdev, struct pmcraid_instance, cdev);
+ filep->private_data = pinstance;
+
+ return 0;
+}
+
+/**
+ * pmcraid_release - char node "release" entry point
+ */
+static int pmcraid_chr_release(struct inode *inode, struct file *filep)
+{
+ struct pmcraid_instance *pinstance =
+ ((struct pmcraid_instance *)filep->private_data);
+
+ filep->private_data = NULL;
+ fasync_helper(-1, filep, 0, &pinstance->aen_queue);
+
+ return 0;
+}
+
+/**
+ * pmcraid_fasync - Async notifier registration from applications
+ *
+ * This function adds the calling process to a driver global queue. When an
+ * event occurs, SIGIO will be sent to all processes in this queue.
+ */
+static int pmcraid_chr_fasync(int fd, struct file *filep, int mode)
+{
+ struct pmcraid_instance *pinstance;
+ int rc;
+
+ pinstance = (struct pmcraid_instance *)filep->private_data;
+ mutex_lock(&pinstance->aen_queue_lock);
+ rc = fasync_helper(fd, filep, mode, &pinstance->aen_queue);
+ mutex_unlock(&pinstance->aen_queue_lock);
+
+ return rc;
+}
+
+
+/**
+ * pmcraid_build_passthrough_ioadls - builds SG elements for passthrough
+ * commands sent over IOCTL interface
+ *
+ * @cmd : pointer to struct pmcraid_cmd
+ * @buflen : length of the request buffer
+ * @direction : data transfer direction
+ *
+ * Return value
+ * 0 on sucess, non-zero error code on failure
+ */
+static int pmcraid_build_passthrough_ioadls(
+ struct pmcraid_cmd *cmd,
+ int buflen,
+ int direction
+)
+{
+ struct pmcraid_sglist *sglist = NULL;
+ struct scatterlist *sg = NULL;
+ struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
+ struct pmcraid_ioadl_desc *ioadl;
+ int i;
+
+ sglist = pmcraid_alloc_sglist(buflen);
+
+ if (!sglist) {
+ pmcraid_err("can't allocate memory for passthrough SGls\n");
+ return -ENOMEM;
+ }
+
+ sglist->num_dma_sg = pci_map_sg(cmd->drv_inst->pdev,
+ sglist->scatterlist,
+ sglist->num_sg, direction);
+
+ if (!sglist->num_dma_sg || sglist->num_dma_sg > PMCRAID_MAX_IOADLS) {
+ dev_err(&cmd->drv_inst->pdev->dev,
+ "Failed to map passthrough buffer!\n");
+ pmcraid_free_sglist(sglist);
+ return -EIO;
+ }
+
+ cmd->sglist = sglist;
+ ioarcb->request_flags0 |= NO_LINK_DESCS;
+
+ ioadl = pmcraid_init_ioadls(cmd, sglist->num_dma_sg);
+
+ /* Initialize IOADL descriptor addresses */
+ for_each_sg(sglist->scatterlist, sg, sglist->num_dma_sg, i) {
+ ioadl[i].data_len = cpu_to_le32(sg_dma_len(sg));
+ ioadl[i].address = cpu_to_le64(sg_dma_address(sg));
+ ioadl[i].flags = 0;
+ }
+
+ /* setup the last descriptor */
+ ioadl[i - 1].flags = cpu_to_le32(IOADL_FLAGS_LAST_DESC);
+
+ return 0;
+}
+
+
+/**
+ * pmcraid_release_passthrough_ioadls - release passthrough ioadls
+ *
+ * @cmd: pointer to struct pmcraid_cmd for which ioadls were allocated
+ * @buflen: size of the request buffer
+ * @direction: data transfer direction
+ *
+ * Return value
+ * 0 on sucess, non-zero error code on failure
+ */
+static void pmcraid_release_passthrough_ioadls(
+ struct pmcraid_cmd *cmd,
+ int buflen,
+ int direction
+)
+{
+ struct pmcraid_sglist *sglist = cmd->sglist;
+
+ if (buflen > 0) {
+ pci_unmap_sg(cmd->drv_inst->pdev,
+ sglist->scatterlist,
+ sglist->num_sg,
+ direction);
+ pmcraid_free_sglist(sglist);
+ cmd->sglist = NULL;
+ }
+}
+
+/**
+ * pmcraid_ioctl_passthrough - handling passthrough IOCTL commands
+ *
+ * @pinstance: pointer to adapter instance structure
+ * @cmd: ioctl code
+ * @arg: pointer to pmcraid_passthrough_buffer user buffer
+ *
+ * Return value
+ * 0 on sucess, non-zero error code on failure
+ */
+static long pmcraid_ioctl_passthrough(
+ struct pmcraid_instance *pinstance,
+ unsigned int ioctl_cmd,
+ unsigned int buflen,
+ unsigned long arg
+)
+{
+ struct pmcraid_passthrough_ioctl_buffer *buffer;
+ struct pmcraid_ioarcb *ioarcb;
+ struct pmcraid_cmd *cmd;
+ struct pmcraid_cmd *cancel_cmd;
+ unsigned long request_buffer;
+ unsigned long request_offset;
+ unsigned long lock_flags;
+ int request_size;
+ int buffer_size;
+ u8 access, direction;
+ int rc = 0;
+
+ /* If IOA reset is in progress, wait 10 secs for reset to complete */
+ if (pinstance->ioa_reset_in_progress) {
+ rc = wait_event_interruptible_timeout(
+ pinstance->reset_wait_q,
+ !pinstance->ioa_reset_in_progress,
+ msecs_to_jiffies(10000));
+
+ if (!rc)
+ return -ETIMEDOUT;
+ else if (rc < 0)
+ return -ERESTARTSYS;
+ }
+
+ /* If adapter is not in operational state, return error */
+ if (pinstance->ioa_state != IOA_STATE_OPERATIONAL) {
+ pmcraid_err("IOA is not operational\n");
+ return -ENOTTY;
+ }
+
+ buffer_size = sizeof(struct pmcraid_passthrough_ioctl_buffer);
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+
+ if (!buffer) {
+ pmcraid_err("no memory for passthrough buffer\n");
+ return -ENOMEM;
+ }
+
+ request_offset =
+ offsetof(struct pmcraid_passthrough_ioctl_buffer, request_buffer);
+
+ request_buffer = arg + request_offset;
+
+ rc = __copy_from_user(buffer,
+ (struct pmcraid_passthrough_ioctl_buffer *) arg,
+ sizeof(struct pmcraid_passthrough_ioctl_buffer));
+ if (rc) {
+ pmcraid_err("ioctl: can't copy passthrough buffer\n");
+ rc = -EFAULT;
+ goto out_free_buffer;
+ }
+
+ request_size = buffer->ioarcb.data_transfer_length;
+
+ if (buffer->ioarcb.request_flags0 & TRANSFER_DIR_WRITE) {
+ access = VERIFY_READ;
+ direction = DMA_TO_DEVICE;
+ } else {
+ access = VERIFY_WRITE;
+ direction = DMA_FROM_DEVICE;
+ }
+
+ if (request_size > 0) {
+ rc = access_ok(access, arg, request_offset + request_size);
+
+ if (!rc) {
+ rc = -EFAULT;
+ goto out_free_buffer;
+ }
+ }
+
+ /* check if we have any additional command parameters */
+ if (buffer->ioarcb.add_cmd_param_length > PMCRAID_ADD_CMD_PARAM_LEN) {
+ rc = -EINVAL;
+ goto out_free_buffer;
+ }
+
+ cmd = pmcraid_get_free_cmd(pinstance);
+
+ if (!cmd) {
+ pmcraid_err("free command block is not available\n");
+ rc = -ENOMEM;
+ goto out_free_buffer;
+ }
+
+ cmd->scsi_cmd = NULL;
+ ioarcb = &(cmd->ioa_cb->ioarcb);
+
+ /* Copy the user-provided IOARCB stuff field by field */
+ ioarcb->resource_handle = buffer->ioarcb.resource_handle;
+ ioarcb->data_transfer_length = buffer->ioarcb.data_transfer_length;
+ ioarcb->cmd_timeout = buffer->ioarcb.cmd_timeout;
+ ioarcb->request_type = buffer->ioarcb.request_type;
+ ioarcb->request_flags0 = buffer->ioarcb.request_flags0;
+ ioarcb->request_flags1 = buffer->ioarcb.request_flags1;
+ memcpy(ioarcb->cdb, buffer->ioarcb.cdb, PMCRAID_MAX_CDB_LEN);
+
+ if (buffer->ioarcb.add_cmd_param_length) {
+ ioarcb->add_cmd_param_length =
+ buffer->ioarcb.add_cmd_param_length;
+ ioarcb->add_cmd_param_offset =
+ buffer->ioarcb.add_cmd_param_offset;
+ memcpy(ioarcb->add_data.u.add_cmd_params,
+ buffer->ioarcb.add_data.u.add_cmd_params,
+ buffer->ioarcb.add_cmd_param_length);
+ }
+
+ if (request_size) {
+ rc = pmcraid_build_passthrough_ioadls(cmd,
+ request_size,
+ direction);
+ if (rc) {
+ pmcraid_err("couldn't build passthrough ioadls\n");
+ goto out_free_buffer;
+ }
+ }
+
+ /* If data is being written into the device, copy the data from user
+ * buffers
+ */
+ if (direction == DMA_TO_DEVICE && request_size > 0) {
+ rc = pmcraid_copy_sglist(cmd->sglist,
+ request_buffer,
+ request_size,
+ direction);
+ if (rc) {
+ pmcraid_err("failed to copy user buffer\n");
+ goto out_free_sglist;
+ }
+ }
+
+ /* passthrough ioctl is a blocking command so, put the user to sleep
+ * until timeout. Note that a timeout value of 0 means, do timeout.
+ */
+ cmd->cmd_done = pmcraid_internal_done;
+ init_completion(&cmd->wait_for_completion);
+ cmd->completion_req = 1;
+
+ pmcraid_info("command(%d) (CDB[0] = %x) for %x\n",
+ le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle) >> 2,
+ cmd->ioa_cb->ioarcb.cdb[0],
+ le32_to_cpu(cmd->ioa_cb->ioarcb.resource_handle));
+
+ spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
+ _pmcraid_fire_command(cmd);
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+
+ /* If command timeout is specified put caller to wait till that time,
+ * otherwise it would be blocking wait. If command gets timed out, it
+ * will be aborted.
+ */
+ if (buffer->ioarcb.cmd_timeout == 0) {
+ wait_for_completion(&cmd->wait_for_completion);
+ } else if (!wait_for_completion_timeout(
+ &cmd->wait_for_completion,
+ msecs_to_jiffies(buffer->ioarcb.cmd_timeout * 1000))) {
+
+ pmcraid_info("aborting cmd %d (CDB[0] = %x) due to timeout\n",
+ le32_to_cpu(cmd->ioa_cb->ioarcb.response_handle >> 2),
+ cmd->ioa_cb->ioarcb.cdb[0]);
+
+ rc = -ETIMEDOUT;
+ spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
+ cancel_cmd = pmcraid_abort_cmd(cmd);
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+
+ if (cancel_cmd) {
+ wait_for_completion(&cancel_cmd->wait_for_completion);
+ pmcraid_return_cmd(cancel_cmd);
+ }
+
+ goto out_free_sglist;
+ }
+
+ /* If the command failed for any reason, copy entire IOASA buffer and
+ * return IOCTL success. If copying IOASA to user-buffer fails, return
+ * EFAULT
+ */
+ if (le32_to_cpu(cmd->ioa_cb->ioasa.ioasc)) {
+
+ void *ioasa =
+ (void *)(arg +
+ offsetof(struct pmcraid_passthrough_ioctl_buffer, ioasa));
+
+ pmcraid_info("command failed with %x\n",
+ le32_to_cpu(cmd->ioa_cb->ioasa.ioasc));
+ if (copy_to_user(ioasa, &cmd->ioa_cb->ioasa,
+ sizeof(struct pmcraid_ioasa))) {
+ pmcraid_err("failed to copy ioasa buffer to user\n");
+ rc = -EFAULT;
+ }
+ }
+ /* If the data transfer was from device, copy the data onto user
+ * buffers
+ */
+ else if (direction == DMA_FROM_DEVICE && request_size > 0) {
+ rc = pmcraid_copy_sglist(cmd->sglist,
+ request_buffer,
+ request_size,
+ direction);
+ if (rc) {
+ pmcraid_err("failed to copy user buffer\n");
+ rc = -EFAULT;
+ }
+ }
+
+out_free_sglist:
+ pmcraid_release_passthrough_ioadls(cmd, request_size, direction);
+ pmcraid_return_cmd(cmd);
+
+out_free_buffer:
+ kfree(buffer);
+
+ return rc;
+}
+
+
+
+
+/**
+ * pmcraid_ioctl_driver - ioctl handler for commands handled by driver itself
+ *
+ * @pinstance: pointer to adapter instance structure
+ * @cmd: ioctl command passed in
+ * @buflen: length of user_buffer
+ * @user_buffer: user buffer pointer
+ *
+ * Return Value
+ * 0 in case of success, otherwise appropriate error code
+ */
+static long pmcraid_ioctl_driver(
+ struct pmcraid_instance *pinstance,
+ unsigned int cmd,
+ unsigned int buflen,
+ void __user *user_buffer
+)
+{
+ int rc = -ENOSYS;
+
+ if (!access_ok(VERIFY_READ, user_buffer, _IOC_SIZE(cmd))) {
+ pmcraid_err("ioctl_driver: access fault in request buffer \n");
+ return -EFAULT;
+ }
+
+ switch (cmd) {
+ case PMCRAID_IOCTL_RESET_ADAPTER:
+ pmcraid_reset_bringup(pinstance);
+ rc = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+/**
+ * pmcraid_check_ioctl_buffer - check for proper access to user buffer
+ *
+ * @cmd: ioctl command
+ * @arg: user buffer
+ * @hdr: pointer to kernel memory for pmcraid_ioctl_header
+ *
+ * Return Value
+ * negetive error code if there are access issues, otherwise zero.
+ * Upon success, returns ioctl header copied out of user buffer.
+ */
+
+static int pmcraid_check_ioctl_buffer(
+ int cmd,
+ void __user *arg,
+ struct pmcraid_ioctl_header *hdr
+)
+{
+ int rc = 0;
+ int access = VERIFY_READ;
+
+ if (copy_from_user(hdr, arg, sizeof(struct pmcraid_ioctl_header))) {
+ pmcraid_err("couldn't copy ioctl header from user buffer\n");
+ return -EFAULT;
+ }
+
+ /* check for valid driver signature */
+ rc = memcmp(hdr->signature,
+ PMCRAID_IOCTL_SIGNATURE,
+ sizeof(hdr->signature));
+ if (rc) {
+ pmcraid_err("signature verification failed\n");
+ return -EINVAL;
+ }
+
+ /* buffer length can't be negetive */
+ if (hdr->buffer_length < 0) {
+ pmcraid_err("ioctl: invalid buffer length specified\n");
+ return -EINVAL;
+ }
+
+ /* check for appropriate buffer access */
+ if ((_IOC_DIR(cmd) & _IOC_READ) == _IOC_READ)
+ access = VERIFY_WRITE;
+
+ rc = access_ok(access,
+ (arg + sizeof(struct pmcraid_ioctl_header)),
+ hdr->buffer_length);
+ if (!rc) {
+ pmcraid_err("access failed for user buffer of size %d\n",
+ hdr->buffer_length);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/**
+ * pmcraid_ioctl - char node ioctl entry point
+ */
+static long pmcraid_chr_ioctl(
+ struct file *filep,
+ unsigned int cmd,
+ unsigned long arg
+)
+{
+ struct pmcraid_instance *pinstance = NULL;
+ struct pmcraid_ioctl_header *hdr = NULL;
+ int retval = -ENOTTY;
+
+ hdr = kmalloc(GFP_KERNEL, sizeof(struct pmcraid_ioctl_header));
+
+ if (!hdr) {
+ pmcraid_err("faile to allocate memory for ioctl header\n");
+ return -ENOMEM;
+ }
+
+ retval = pmcraid_check_ioctl_buffer(cmd, (void *)arg, hdr);
+
+ if (retval) {
+ pmcraid_info("chr_ioctl: header check failed\n");
+ kfree(hdr);
+ return retval;
+ }
+
+ pinstance = (struct pmcraid_instance *)filep->private_data;
+
+ if (!pinstance) {
+ pmcraid_info("adapter instance is not found\n");
+ kfree(hdr);
+ return -ENOTTY;
+ }
+
+ switch (_IOC_TYPE(cmd)) {
+
+ case PMCRAID_PASSTHROUGH_IOCTL:
+ /* If ioctl code is to download microcode, we need to block
+ * mid-layer requests.
+ */
+ if (cmd == PMCRAID_IOCTL_DOWNLOAD_MICROCODE)
+ scsi_block_requests(pinstance->host);
+
+ retval = pmcraid_ioctl_passthrough(pinstance,
+ cmd,
+ hdr->buffer_length,
+ arg);
+
+ if (cmd == PMCRAID_IOCTL_DOWNLOAD_MICROCODE)
+ scsi_unblock_requests(pinstance->host);
+ break;
+
+ case PMCRAID_DRIVER_IOCTL:
+ arg += sizeof(struct pmcraid_ioctl_header);
+ retval = pmcraid_ioctl_driver(pinstance,
+ cmd,
+ hdr->buffer_length,
+ (void __user *)arg);
+ break;
+
+ default:
+ retval = -ENOTTY;
+ break;
+ }
+
+ kfree(hdr);
+
+ return retval;
+}
+
+/**
+ * File operations structure for management interface
+ */
+static const struct file_operations pmcraid_fops = {
+ .owner = THIS_MODULE,
+ .open = pmcraid_chr_open,
+ .release = pmcraid_chr_release,
+ .fasync = pmcraid_chr_fasync,
+ .unlocked_ioctl = pmcraid_chr_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = pmcraid_chr_ioctl,
+#endif
+};
+
+
+
+
+/**
+ * pmcraid_show_log_level - Display adapter's error logging level
+ * @dev: class device struct
+ * @buf: buffer
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ */
+static ssize_t pmcraid_show_log_level(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct pmcraid_instance *pinstance =
+ (struct pmcraid_instance *)shost->hostdata;
+ return snprintf(buf, PAGE_SIZE, "%d\n", pinstance->current_log_level);
+}
+
+/**
+ * pmcraid_store_log_level - Change the adapter's error logging level
+ * @dev: class device struct
+ * @buf: buffer
+ * @count: not used
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ */
+static ssize_t pmcraid_store_log_level(
+ struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count
+)
+{
+ struct Scsi_Host *shost;
+ struct pmcraid_instance *pinstance;
+ unsigned long val;
+
+ if (strict_strtoul(buf, 10, &val))
+ return -EINVAL;
+ /* log-level should be from 0 to 2 */
+ if (val > 2)
+ return -EINVAL;
+
+ shost = class_to_shost(dev);
+ pinstance = (struct pmcraid_instance *)shost->hostdata;
+ pinstance->current_log_level = val;
+
+ return strlen(buf);
+}
+
+static struct device_attribute pmcraid_log_level_attr = {
+ .attr = {
+ .name = "log_level",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .show = pmcraid_show_log_level,
+ .store = pmcraid_store_log_level,
+};
+
+/**
+ * pmcraid_show_drv_version - Display driver version
+ * @dev: class device struct
+ * @buf: buffer
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ */
+static ssize_t pmcraid_show_drv_version(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf
+)
+{
+ return snprintf(buf, PAGE_SIZE, "version: %s, build date: %s\n",
+ PMCRAID_DRIVER_VERSION, PMCRAID_DRIVER_DATE);
+}
+
+static struct device_attribute pmcraid_driver_version_attr = {
+ .attr = {
+ .name = "drv_version",
+ .mode = S_IRUGO,
+ },
+ .show = pmcraid_show_drv_version,
+};
+
+/**
+ * pmcraid_show_io_adapter_id - Display driver assigned adapter id
+ * @dev: class device struct
+ * @buf: buffer
+ *
+ * Return value:
+ * number of bytes printed to buffer
+ */
+static ssize_t pmcraid_show_adapter_id(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf
+)
+{
+ struct Scsi_Host *shost = class_to_shost(dev);
+ struct pmcraid_instance *pinstance =
+ (struct pmcraid_instance *)shost->hostdata;
+ u32 adapter_id = (pinstance->pdev->bus->number << 8) |
+ pinstance->pdev->devfn;
+ u32 aen_group = pmcraid_event_family.id;
+
+ return snprintf(buf, PAGE_SIZE,
+ "adapter id: %d\nminor: %d\naen group: %d\n",
+ adapter_id, MINOR(pinstance->cdev.dev), aen_group);
+}
+
+static struct device_attribute pmcraid_adapter_id_attr = {
+ .attr = {
+ .name = "adapter_id",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .show = pmcraid_show_adapter_id,
+};
+
+static struct device_attribute *pmcraid_host_attrs[] = {
+ &pmcraid_log_level_attr,
+ &pmcraid_driver_version_attr,
+ &pmcraid_adapter_id_attr,
+ NULL,
+};
+
+
+/* host template structure for pmcraid driver */
+static struct scsi_host_template pmcraid_host_template = {
+ .module = THIS_MODULE,
+ .name = PMCRAID_DRIVER_NAME,
+ .queuecommand = pmcraid_queuecommand,
+ .eh_abort_handler = pmcraid_eh_abort_handler,
+ .eh_bus_reset_handler = pmcraid_eh_bus_reset_handler,
+ .eh_target_reset_handler = pmcraid_eh_target_reset_handler,
+ .eh_device_reset_handler = pmcraid_eh_device_reset_handler,
+ .eh_host_reset_handler = pmcraid_eh_host_reset_handler,
+
+ .slave_alloc = pmcraid_slave_alloc,
+ .slave_configure = pmcraid_slave_configure,
+ .slave_destroy = pmcraid_slave_destroy,
+ .change_queue_depth = pmcraid_change_queue_depth,
+ .change_queue_type = pmcraid_change_queue_type,
+ .can_queue = PMCRAID_MAX_IO_CMD,
+ .this_id = -1,
+ .sg_tablesize = PMCRAID_MAX_IOADLS,
+ .max_sectors = PMCRAID_IOA_MAX_SECTORS,
+ .cmd_per_lun = PMCRAID_MAX_CMD_PER_LUN,
+ .use_clustering = ENABLE_CLUSTERING,
+ .shost_attrs = pmcraid_host_attrs,
+ .proc_name = PMCRAID_DRIVER_NAME
+};
+
+/**
+ * pmcraid_isr_common - Common interrupt handler routine
+ *
+ * @pinstance: pointer to adapter instance
+ * @intrs: active interrupts (contents of ioa_host_interrupt register)
+ * @hrrq_id: Host RRQ index
+ *
+ * Return Value
+ * none
+ */
+static void pmcraid_isr_common(
+ struct pmcraid_instance *pinstance,
+ u32 intrs,
+ int hrrq_id
+)
+{
+ u32 intrs_clear =
+ (intrs & INTRS_CRITICAL_OP_IN_PROGRESS) ? intrs
+ : INTRS_HRRQ_VALID;
+ iowrite32(intrs_clear,
+ pinstance->int_regs.ioa_host_interrupt_clr_reg);
+ intrs = ioread32(pinstance->int_regs.ioa_host_interrupt_reg);
+
+ /* hrrq valid bit was set, schedule tasklet to handle the response */
+ if (intrs_clear == INTRS_HRRQ_VALID)
+ tasklet_schedule(&(pinstance->isr_tasklet[hrrq_id]));
+}
+
+/**
+ * pmcraid_isr - implements interrupt handling routine
+ *
+ * @irq: interrupt vector number
+ * @dev_id: pointer hrrq_vector
+ *
+ * Return Value
+ * IRQ_HANDLED if interrupt is handled or IRQ_NONE if ignored
+ */
+static irqreturn_t pmcraid_isr(int irq, void *dev_id)
+{
+ struct pmcraid_isr_param *hrrq_vector;
+ struct pmcraid_instance *pinstance;
+ unsigned long lock_flags;
+ u32 intrs;
+
+ /* In case of legacy interrupt mode where interrupts are shared across
+ * isrs, it may be possible that the current interrupt is not from IOA
+ */
+ if (!dev_id) {
+ printk(KERN_INFO "%s(): NULL host pointer\n", __func__);
+ return IRQ_NONE;
+ }
+
+ hrrq_vector = (struct pmcraid_isr_param *)dev_id;
+ pinstance = hrrq_vector->drv_inst;
+
+ /* Acquire the lock (currently host_lock) while processing interrupts.
+ * This interval is small as most of the response processing is done by
+ * tasklet without the lock.
+ */
+ spin_lock_irqsave(pinstance->host->host_lock, lock_flags);
+ intrs = pmcraid_read_interrupts(pinstance);
+
+ if (unlikely((intrs & PMCRAID_PCI_INTERRUPTS) == 0)) {
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+ return IRQ_NONE;
+ }
+
+ /* Any error interrupts including unit_check, initiate IOA reset.
+ * In case of unit check indicate to reset_sequence that IOA unit
+ * checked and prepare for a dump during reset sequence
+ */
+ if (intrs & PMCRAID_ERROR_INTERRUPTS) {
+
+ if (intrs & INTRS_IOA_UNIT_CHECK)
+ pinstance->ioa_unit_check = 1;
+
+ iowrite32(intrs,
+ pinstance->int_regs.ioa_host_interrupt_clr_reg);
+ pmcraid_err("ISR: error interrupts: %x initiating reset\n",
+ intrs);
+ intrs = ioread32(pinstance->int_regs.ioa_host_interrupt_reg);
+ pmcraid_initiate_reset(pinstance);
+ } else {
+ pmcraid_isr_common(pinstance, intrs, hrrq_vector->hrrq_id);
+ }
+
+ spin_unlock_irqrestore(pinstance->host->host_lock, lock_flags);
+
+ return IRQ_HANDLED;
+}
+
+
+/**
+ * pmcraid_worker_function - worker thread function
+ *
+ * @workp: pointer to struct work queue
+ *
+ * Return Value
+ * None
+ */
+
+static void pmcraid_worker_function(struct work_struct *workp)
+{
+ struct pmcraid_instance *pinstance;
+ struct pmcraid_resource_entry *res;
+ struct pmcraid_resource_entry *temp;
+ struct scsi_device *sdev;
+ unsigned long lock_flags;
+ unsigned long host_lock_flags;
+ u8 bus, target, lun;
+
+ pinstance = container_of(workp, struct pmcraid_instance, worker_q);
+ /* add resources only after host is added into system */
+ if (!atomic_read(&pinstance->expose_resources))
+ return;
+
+ spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
+ list_for_each_entry_safe(res, temp, &pinstance->used_res_q, queue) {
+
+ if (res->change_detected == RES_CHANGE_DEL && res->scsi_dev) {
+ sdev = res->scsi_dev;
+
+ /* host_lock must be held before calling
+ * scsi_device_get
+ */
+ spin_lock_irqsave(pinstance->host->host_lock,
+ host_lock_flags);
+ if (!scsi_device_get(sdev)) {
+ spin_unlock_irqrestore(
+ pinstance->host->host_lock,
+ host_lock_flags);
+ pmcraid_info("deleting %x from midlayer\n",
+ res->cfg_entry.resource_address);
+ list_move_tail(&res->queue,
+ &pinstance->free_res_q);
+ spin_unlock_irqrestore(
+ &pinstance->resource_lock,
+ lock_flags);
+ scsi_remove_device(sdev);
+ scsi_device_put(sdev);
+ spin_lock_irqsave(&pinstance->resource_lock,
+ lock_flags);
+ res->change_detected = 0;
+ } else {
+ spin_unlock_irqrestore(
+ pinstance->host->host_lock,
+ host_lock_flags);
+ }
+ }
+ }
+
+ list_for_each_entry(res, &pinstance->used_res_q, queue) {
+
+ if (res->change_detected == RES_CHANGE_ADD) {
+
+ if (!pmcraid_expose_resource(&res->cfg_entry))
+ continue;
+
+ if (RES_IS_VSET(res->cfg_entry)) {
+ bus = PMCRAID_VSET_BUS_ID;
+ target = res->cfg_entry.unique_flags1;
+ lun = PMCRAID_VSET_LUN_ID;
+ } else {
+ bus = PMCRAID_PHYS_BUS_ID;
+ target =
+ RES_TARGET(
+ res->cfg_entry.resource_address);
+ lun = RES_LUN(res->cfg_entry.resource_address);
+ }
+
+ res->change_detected = 0;
+ spin_unlock_irqrestore(&pinstance->resource_lock,
+ lock_flags);
+ scsi_add_device(pinstance->host, bus, target, lun);
+ spin_lock_irqsave(&pinstance->resource_lock,
+ lock_flags);
+ }
+ }
+
+ spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags);
+}
+
+/**
+ * pmcraid_tasklet_function - Tasklet function
+ *
+ * @instance: pointer to msix param structure
+ *
+ * Return Value
+ * None
+ */
+void pmcraid_tasklet_function(unsigned long instance)
+{
+ struct pmcraid_isr_param *hrrq_vector;
+ struct pmcraid_instance *pinstance;
+ unsigned long hrrq_lock_flags;
+ unsigned long pending_lock_flags;
+ unsigned long host_lock_flags;
+ spinlock_t *lockp; /* hrrq buffer lock */
+ int id;
+ u32 intrs;
+ __le32 resp;
+
+ hrrq_vector = (struct pmcraid_isr_param *)instance;
+ pinstance = hrrq_vector->drv_inst;
+ id = hrrq_vector->hrrq_id;
+ lockp = &(pinstance->hrrq_lock[id]);
+ intrs = pmcraid_read_interrupts(pinstance);
+
+ /* If interrupts was as part of the ioa initialization, clear and mask
+ * it. Delete the timer and wakeup the reset engine to proceed with
+ * reset sequence
+ */
+ if (intrs & INTRS_TRANSITION_TO_OPERATIONAL) {
+ iowrite32(INTRS_TRANSITION_TO_OPERATIONAL,
+ pinstance->int_regs.ioa_host_interrupt_mask_reg);
+ iowrite32(INTRS_TRANSITION_TO_OPERATIONAL,
+ pinstance->int_regs.ioa_host_interrupt_clr_reg);
+
+ if (pinstance->reset_cmd != NULL) {
+ del_timer(&pinstance->reset_cmd->timer);
+ spin_lock_irqsave(pinstance->host->host_lock,
+ host_lock_flags);
+ pinstance->reset_cmd->cmd_done(pinstance->reset_cmd);
+ spin_unlock_irqrestore(pinstance->host->host_lock,
+ host_lock_flags);
+ }
+ return;
+ }
+
+ /* loop through each of the commands responded by IOA. Each HRRQ buf is
+ * protected by its own lock. Traversals must be done within this lock
+ * as there may be multiple tasklets running on multiple CPUs. Note
+ * that the lock is held just for picking up the response handle and
+ * manipulating hrrq_curr/toggle_bit values.
+ */
+ spin_lock_irqsave(lockp, hrrq_lock_flags);
+
+ resp = le32_to_cpu(*(pinstance->hrrq_curr[id]));
+
+ while ((resp & HRRQ_TOGGLE_BIT) ==
+ pinstance->host_toggle_bit[id]) {
+
+ int cmd_index = resp >> 2;
+ struct pmcraid_cmd *cmd = NULL;
+
+ if (cmd_index < PMCRAID_MAX_CMD) {
+ cmd = pinstance->cmd_list[cmd_index];
+ } else {
+ /* In case of invalid response handle, initiate IOA
+ * reset sequence.
+ */
+ spin_unlock_irqrestore(lockp, hrrq_lock_flags);
+
+ pmcraid_err("Invalid response %d initiating reset\n",
+ cmd_index);
+
+ spin_lock_irqsave(pinstance->host->host_lock,
+ host_lock_flags);
+ pmcraid_initiate_reset(pinstance);
+ spin_unlock_irqrestore(pinstance->host->host_lock,
+ host_lock_flags);
+
+ spin_lock_irqsave(lockp, hrrq_lock_flags);
+ break;
+ }
+
+ if (pinstance->hrrq_curr[id] < pinstance->hrrq_end[id]) {
+ pinstance->hrrq_curr[id]++;
+ } else {
+ pinstance->hrrq_curr[id] = pinstance->hrrq_start[id];
+ pinstance->host_toggle_bit[id] ^= 1u;
+ }
+
+ spin_unlock_irqrestore(lockp, hrrq_lock_flags);
+
+ spin_lock_irqsave(&pinstance->pending_pool_lock,
+ pending_lock_flags);
+ list_del(&cmd->free_list);
+ spin_unlock_irqrestore(&pinstance->pending_pool_lock,
+ pending_lock_flags);
+ del_timer(&cmd->timer);
+ atomic_dec(&pinstance->outstanding_cmds);
+
+ if (cmd->cmd_done == pmcraid_ioa_reset) {
+ spin_lock_irqsave(pinstance->host->host_lock,
+ host_lock_flags);
+ cmd->cmd_done(cmd);
+ spin_unlock_irqrestore(pinstance->host->host_lock,
+ host_lock_flags);
+ } else if (cmd->cmd_done != NULL) {
+ cmd->cmd_done(cmd);
+ }
+ /* loop over until we are done with all responses */
+ spin_lock_irqsave(lockp, hrrq_lock_flags);
+ resp = le32_to_cpu(*(pinstance->hrrq_curr[id]));
+ }
+
+ spin_unlock_irqrestore(lockp, hrrq_lock_flags);
+}
+
+/**
+ * pmcraid_unregister_interrupt_handler - de-register interrupts handlers
+ * @pinstance: pointer to adapter instance structure
+ *
+ * This routine un-registers registered interrupt handler and
+ * also frees irqs/vectors.
+ *
+ * Retun Value
+ * None
+ */
+static
+void pmcraid_unregister_interrupt_handler(struct pmcraid_instance *pinstance)
+{
+ free_irq(pinstance->pdev->irq, &(pinstance->hrrq_vector[0]));
+}
+
+/**
+ * pmcraid_register_interrupt_handler - registers interrupt handler
+ * @pinstance: pointer to per-adapter instance structure
+ *
+ * Return Value
+ * 0 on success, non-zero error code otherwise.
+ */
+static int
+pmcraid_register_interrupt_handler(struct pmcraid_instance *pinstance)
+{
+ struct pci_dev *pdev = pinstance->pdev;
+
+ pinstance->hrrq_vector[0].hrrq_id = 0;
+ pinstance->hrrq_vector[0].drv_inst = pinstance;
+ pinstance->hrrq_vector[0].vector = 0;
+ pinstance->num_hrrq = 1;
+ return request_irq(pdev->irq, pmcraid_isr, IRQF_SHARED,
+ PMCRAID_DRIVER_NAME, &pinstance->hrrq_vector[0]);
+}
+
+/**
+ * pmcraid_release_cmd_blocks - release buufers allocated for command blocks
+ * @pinstance: per adapter instance structure pointer
+ * @max_index: number of buffer blocks to release
+ *
+ * Return Value
+ * None
+ */
+static void
+pmcraid_release_cmd_blocks(struct pmcraid_instance *pinstance, int max_index)
+{
+ int i;
+ for (i = 0; i < max_index; i++) {
+ kmem_cache_free(pinstance->cmd_cachep, pinstance->cmd_list[i]);
+ pinstance->cmd_list[i] = NULL;
+ }
+ kmem_cache_destroy(pinstance->cmd_cachep);
+ pinstance->cmd_cachep = NULL;
+}
+
+/**
+ * pmcraid_release_control_blocks - releases buffers alloced for control blocks
+ * @pinstance: pointer to per adapter instance structure
+ * @max_index: number of buffers (from 0 onwards) to release
+ *
+ * This function assumes that the command blocks for which control blocks are
+ * linked are not released.
+ *
+ * Return Value
+ * None
+ */
+static void
+pmcraid_release_control_blocks(
+ struct pmcraid_instance *pinstance,
+ int max_index
+)
+{
+ int i;
+
+ if (pinstance->control_pool == NULL)
+ return;
+
+ for (i = 0; i < max_index; i++) {
+ pci_pool_free(pinstance->control_pool,
+ pinstance->cmd_list[i]->ioa_cb,
+ pinstance->cmd_list[i]->ioa_cb_bus_addr);
+ pinstance->cmd_list[i]->ioa_cb = NULL;
+ pinstance->cmd_list[i]->ioa_cb_bus_addr = 0;
+ }
+ pci_pool_destroy(pinstance->control_pool);
+ pinstance->control_pool = NULL;
+}
+
+/**
+ * pmcraid_allocate_cmd_blocks - allocate memory for cmd block structures
+ * @pinstance - pointer to per adapter instance structure
+ *
+ * Allocates memory for command blocks using kernel slab allocator.
+ *
+ * Return Value
+ * 0 in case of success; -ENOMEM in case of failure
+ */
+static int __devinit
+pmcraid_allocate_cmd_blocks(struct pmcraid_instance *pinstance)
+{
+ int i;
+
+ sprintf(pinstance->cmd_pool_name, "pmcraid_cmd_pool_%d",
+ pinstance->host->unique_id);
+
+
+ pinstance->cmd_cachep = kmem_cache_create(
+ pinstance->cmd_pool_name,
+ sizeof(struct pmcraid_cmd), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (!pinstance->cmd_cachep)
+ return -ENOMEM;
+
+ for (i = 0; i < PMCRAID_MAX_CMD; i++) {
+ pinstance->cmd_list[i] =
+ kmem_cache_alloc(pinstance->cmd_cachep, GFP_KERNEL);
+ if (!pinstance->cmd_list[i]) {
+ pmcraid_release_cmd_blocks(pinstance, i);
+ return -ENOMEM;
+ }
+ }
+ return 0;
+}
+
+/**
+ * pmcraid_allocate_control_blocks - allocates memory control blocks
+ * @pinstance : pointer to per adapter instance structure
+ *
+ * This function allocates PCI memory for DMAable buffers like IOARCB, IOADLs
+ * and IOASAs. This is called after command blocks are already allocated.
+ *
+ * Return Value
+ * 0 in case it can allocate all control blocks, otherwise -ENOMEM
+ */
+static int __devinit
+pmcraid_allocate_control_blocks(struct pmcraid_instance *pinstance)
+{
+ int i;
+
+ sprintf(pinstance->ctl_pool_name, "pmcraid_control_pool_%d",
+ pinstance->host->unique_id);
+
+ pinstance->control_pool =
+ pci_pool_create(pinstance->ctl_pool_name,
+ pinstance->pdev,
+ sizeof(struct pmcraid_control_block),
+ PMCRAID_IOARCB_ALIGNMENT, 0);
+
+ if (!pinstance->control_pool)
+ return -ENOMEM;
+
+ for (i = 0; i < PMCRAID_MAX_CMD; i++) {
+ pinstance->cmd_list[i]->ioa_cb =
+ pci_pool_alloc(
+ pinstance->control_pool,
+ GFP_KERNEL,
+ &(pinstance->cmd_list[i]->ioa_cb_bus_addr));
+
+ if (!pinstance->cmd_list[i]->ioa_cb) {
+ pmcraid_release_control_blocks(pinstance, i);
+ return -ENOMEM;
+ }
+ memset(pinstance->cmd_list[i]->ioa_cb, 0,
+ sizeof(struct pmcraid_control_block));
+ }
+ return 0;
+}
+
+/**
+ * pmcraid_release_host_rrqs - release memory allocated for hrrq buffer(s)
+ * @pinstance: pointer to per adapter instance structure
+ * @maxindex: size of hrrq buffer pointer array
+ *
+ * Return Value
+ * None
+ */
+static void
+pmcraid_release_host_rrqs(struct pmcraid_instance *pinstance, int maxindex)
+{
+ int i;
+ for (i = 0; i < maxindex; i++) {
+
+ pci_free_consistent(pinstance->pdev,
+ HRRQ_ENTRY_SIZE * PMCRAID_MAX_CMD,
+ pinstance->hrrq_start[i],
+ pinstance->hrrq_start_bus_addr[i]);
+
+ /* reset pointers and toggle bit to zeros */
+ pinstance->hrrq_start[i] = NULL;
+ pinstance->hrrq_start_bus_addr[i] = 0;
+ pinstance->host_toggle_bit[i] = 0;
+ }
+}
+
+/**
+ * pmcraid_allocate_host_rrqs - Allocate and initialize host RRQ buffers
+ * @pinstance: pointer to per adapter instance structure
+ *
+ * Return value
+ * 0 hrrq buffers are allocated, -ENOMEM otherwise.
+ */
+static int __devinit
+pmcraid_allocate_host_rrqs(struct pmcraid_instance *pinstance)
+{
+ int i;
+ int buf_count = PMCRAID_MAX_CMD / pinstance->num_hrrq;
+
+ for (i = 0; i < pinstance->num_hrrq; i++) {
+ int buffer_size = HRRQ_ENTRY_SIZE * buf_count;
+
+ pinstance->hrrq_start[i] =
+ pci_alloc_consistent(
+ pinstance->pdev,
+ buffer_size,
+ &(pinstance->hrrq_start_bus_addr[i]));
+
+ if (pinstance->hrrq_start[i] == 0) {
+ pmcraid_err("could not allocate host rrq: %d\n", i);
+ pmcraid_release_host_rrqs(pinstance, i);
+ return -ENOMEM;
+ }
+
+ memset(pinstance->hrrq_start[i], 0, buffer_size);
+ pinstance->hrrq_curr[i] = pinstance->hrrq_start[i];
+ pinstance->hrrq_end[i] =
+ pinstance->hrrq_start[i] + buf_count - 1;
+ pinstance->host_toggle_bit[i] = 1;
+ spin_lock_init(&pinstance->hrrq_lock[i]);
+ }
+ return 0;
+}
+
+/**
+ * pmcraid_release_hcams - release HCAM buffers
+ *
+ * @pinstance: pointer to per adapter instance structure
+ *
+ * Return value
+ * none
+ */
+static void pmcraid_release_hcams(struct pmcraid_instance *pinstance)
+{
+ if (pinstance->ccn.msg != NULL) {
+ pci_free_consistent(pinstance->pdev,
+ PMCRAID_AEN_HDR_SIZE +
+ sizeof(struct pmcraid_hcam_ccn),
+ pinstance->ccn.msg,
+ pinstance->ccn.baddr);
+
+ pinstance->ccn.msg = NULL;
+ pinstance->ccn.hcam = NULL;
+ pinstance->ccn.baddr = 0;
+ }
+
+ if (pinstance->ldn.msg != NULL) {
+ pci_free_consistent(pinstance->pdev,
+ PMCRAID_AEN_HDR_SIZE +
+ sizeof(struct pmcraid_hcam_ldn),
+ pinstance->ldn.msg,
+ pinstance->ldn.baddr);
+
+ pinstance->ldn.msg = NULL;
+ pinstance->ldn.hcam = NULL;
+ pinstance->ldn.baddr = 0;
+ }
+}
+
+/**
+ * pmcraid_allocate_hcams - allocates HCAM buffers
+ * @pinstance : pointer to per adapter instance structure
+ *
+ * Return Value:
+ * 0 in case of successful allocation, non-zero otherwise
+ */
+static int pmcraid_allocate_hcams(struct pmcraid_instance *pinstance)
+{
+ pinstance->ccn.msg = pci_alloc_consistent(
+ pinstance->pdev,
+ PMCRAID_AEN_HDR_SIZE +
+ sizeof(struct pmcraid_hcam_ccn),
+ &(pinstance->ccn.baddr));
+
+ pinstance->ldn.msg = pci_alloc_consistent(
+ pinstance->pdev,
+ PMCRAID_AEN_HDR_SIZE +
+ sizeof(struct pmcraid_hcam_ldn),
+ &(pinstance->ldn.baddr));
+
+ if (pinstance->ldn.msg == NULL || pinstance->ccn.msg == NULL) {
+ pmcraid_release_hcams(pinstance);
+ } else {
+ pinstance->ccn.hcam =
+ (void *)pinstance->ccn.msg + PMCRAID_AEN_HDR_SIZE;
+ pinstance->ldn.hcam =
+ (void *)pinstance->ldn.msg + PMCRAID_AEN_HDR_SIZE;
+
+ atomic_set(&pinstance->ccn.ignore, 0);
+ atomic_set(&pinstance->ldn.ignore, 0);
+ }
+
+ return (pinstance->ldn.msg == NULL) ? -ENOMEM : 0;
+}
+
+/**
+ * pmcraid_release_config_buffers - release config.table buffers
+ * @pinstance: pointer to per adapter instance structure
+ *
+ * Return Value
+ * none
+ */
+static void pmcraid_release_config_buffers(struct pmcraid_instance *pinstance)
+{
+ if (pinstance->cfg_table != NULL &&
+ pinstance->cfg_table_bus_addr != 0) {
+ pci_free_consistent(pinstance->pdev,
+ sizeof(struct pmcraid_config_table),
+ pinstance->cfg_table,
+ pinstance->cfg_table_bus_addr);
+ pinstance->cfg_table = NULL;
+ pinstance->cfg_table_bus_addr = 0;
+ }
+
+ if (pinstance->res_entries != NULL) {
+ int i;
+
+ for (i = 0; i < PMCRAID_MAX_RESOURCES; i++)
+ list_del(&pinstance->res_entries[i].queue);
+ kfree(pinstance->res_entries);
+ pinstance->res_entries = NULL;
+ }
+
+ pmcraid_release_hcams(pinstance);
+}
+
+/**
+ * pmcraid_allocate_config_buffers - allocates DMAable memory for config table
+ * @pinstance : pointer to per adapter instance structure
+ *
+ * Return Value
+ * 0 for successful allocation, -ENOMEM for any failure
+ */
+static int __devinit
+pmcraid_allocate_config_buffers(struct pmcraid_instance *pinstance)
+{
+ int i;
+
+ pinstance->res_entries =
+ kzalloc(sizeof(struct pmcraid_resource_entry) *
+ PMCRAID_MAX_RESOURCES, GFP_KERNEL);
+
+ if (NULL == pinstance->res_entries) {
+ pmcraid_err("failed to allocate memory for resource table\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < PMCRAID_MAX_RESOURCES; i++)
+ list_add_tail(&pinstance->res_entries[i].queue,
+ &pinstance->free_res_q);
+
+ pinstance->cfg_table =
+ pci_alloc_consistent(pinstance->pdev,
+ sizeof(struct pmcraid_config_table),
+ &pinstance->cfg_table_bus_addr);
+
+ if (NULL == pinstance->cfg_table) {
+ pmcraid_err("couldn't alloc DMA memory for config table\n");
+ pmcraid_release_config_buffers(pinstance);
+ return -ENOMEM;
+ }
+
+ if (pmcraid_allocate_hcams(pinstance)) {
+ pmcraid_err("could not alloc DMA memory for HCAMS\n");
+ pmcraid_release_config_buffers(pinstance);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * pmcraid_init_tasklets - registers tasklets for response handling
+ *
+ * @pinstance: pointer adapter instance structure
+ *
+ * Return value
+ * none
+ */
+static void pmcraid_init_tasklets(struct pmcraid_instance *pinstance)
+{
+ int i;
+ for (i = 0; i < pinstance->num_hrrq; i++)
+ tasklet_init(&pinstance->isr_tasklet[i],
+ pmcraid_tasklet_function,
+ (unsigned long)&pinstance->hrrq_vector[i]);
+}
+
+/**
+ * pmcraid_kill_tasklets - destroys tasklets registered for response handling
+ *
+ * @pinstance: pointer to adapter instance structure
+ *
+ * Return value
+ * none
+ */
+static void pmcraid_kill_tasklets(struct pmcraid_instance *pinstance)
+{
+ int i;
+ for (i = 0; i < pinstance->num_hrrq; i++)
+ tasklet_kill(&pinstance->isr_tasklet[i]);
+}
+
+/**
+ * pmcraid_init_buffers - allocates memory and initializes various structures
+ * @pinstance: pointer to per adapter instance structure
+ *
+ * This routine pre-allocates memory based on the type of block as below:
+ * cmdblocks(PMCRAID_MAX_CMD): kernel memory using kernel's slab_allocator,
+ * IOARCBs(PMCRAID_MAX_CMD) : DMAable memory, using pci pool allocator
+ * config-table entries : DMAable memory using pci_alloc_consistent
+ * HostRRQs : DMAable memory, using pci_alloc_consistent
+ *
+ * Return Value
+ * 0 in case all of the blocks are allocated, -ENOMEM otherwise.
+ */
+static int __devinit pmcraid_init_buffers(struct pmcraid_instance *pinstance)
+{
+ int i;
+
+ if (pmcraid_allocate_host_rrqs(pinstance)) {
+ pmcraid_err("couldn't allocate memory for %d host rrqs\n",
+ pinstance->num_hrrq);
+ return -ENOMEM;
+ }
+
+ if (pmcraid_allocate_config_buffers(pinstance)) {
+ pmcraid_err("couldn't allocate memory for config buffers\n");
+ pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq);
+ return -ENOMEM;
+ }
+
+ if (pmcraid_allocate_cmd_blocks(pinstance)) {
+ pmcraid_err("couldn't allocate memory for cmd blocks \n");
+ pmcraid_release_config_buffers(pinstance);
+ pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq);
+ return -ENOMEM;
+ }
+
+ if (pmcraid_allocate_control_blocks(pinstance)) {
+ pmcraid_err("couldn't allocate memory control blocks \n");
+ pmcraid_release_config_buffers(pinstance);
+ pmcraid_release_cmd_blocks(pinstance, PMCRAID_MAX_CMD);
+ pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq);
+ return -ENOMEM;
+ }
+
+ /* Initialize all the command blocks and add them to free pool. No
+ * need to lock (free_pool_lock) as this is done in initialization
+ * itself
+ */
+ for (i = 0; i < PMCRAID_MAX_CMD; i++) {
+ struct pmcraid_cmd *cmdp = pinstance->cmd_list[i];
+ pmcraid_init_cmdblk(cmdp, i);
+ cmdp->drv_inst = pinstance;
+ list_add_tail(&cmdp->free_list, &pinstance->free_cmd_pool);
+ }
+
+ return 0;
+}
+
+/**
+ * pmcraid_reinit_buffers - resets various buffer pointers
+ * @pinstance: pointer to adapter instance
+ * Return value
+ * none
+ */
+static void pmcraid_reinit_buffers(struct pmcraid_instance *pinstance)
+{
+ int i;
+ int buffer_size = HRRQ_ENTRY_SIZE * PMCRAID_MAX_CMD;
+
+ for (i = 0; i < pinstance->num_hrrq; i++) {
+ memset(pinstance->hrrq_start[i], 0, buffer_size);
+ pinstance->hrrq_curr[i] = pinstance->hrrq_start[i];
+ pinstance->hrrq_end[i] =
+ pinstance->hrrq_start[i] + PMCRAID_MAX_CMD - 1;
+ pinstance->host_toggle_bit[i] = 1;
+ }
+}
+
+/**
+ * pmcraid_init_instance - initialize per instance data structure
+ * @pdev: pointer to pci device structure
+ * @host: pointer to Scsi_Host structure
+ * @mapped_pci_addr: memory mapped IOA configuration registers
+ *
+ * Return Value
+ * 0 on success, non-zero in case of any failure
+ */
+static int __devinit pmcraid_init_instance(
+ struct pci_dev *pdev,
+ struct Scsi_Host *host,
+ void __iomem *mapped_pci_addr
+)
+{
+ struct pmcraid_instance *pinstance =
+ (struct pmcraid_instance *)host->hostdata;
+
+ pinstance->host = host;
+ pinstance->pdev = pdev;
+
+ /* Initialize register addresses */
+ pinstance->mapped_dma_addr = mapped_pci_addr;
+
+ /* Initialize chip-specific details */
+ {
+ struct pmcraid_chip_details *chip_cfg = pinstance->chip_cfg;
+ struct pmcraid_interrupts *pint_regs = &pinstance->int_regs;
+
+ pinstance->ioarrin = mapped_pci_addr + chip_cfg->ioarrin;
+
+ pint_regs->ioa_host_interrupt_reg =
+ mapped_pci_addr + chip_cfg->ioa_host_intr;
+ pint_regs->ioa_host_interrupt_clr_reg =
+ mapped_pci_addr + chip_cfg->ioa_host_intr_clr;
+ pint_regs->host_ioa_interrupt_reg =
+ mapped_pci_addr + chip_cfg->host_ioa_intr;
+ pint_regs->host_ioa_interrupt_clr_reg =
+ mapped_pci_addr + chip_cfg->host_ioa_intr_clr;
+
+ /* Current version of firmware exposes interrupt mask set
+ * and mask clr registers through memory mapped bar0.
+ */
+ pinstance->mailbox = mapped_pci_addr + chip_cfg->mailbox;
+ pinstance->ioa_status = mapped_pci_addr + chip_cfg->ioastatus;
+ pint_regs->ioa_host_interrupt_mask_reg =
+ mapped_pci_addr + chip_cfg->ioa_host_mask;
+ pint_regs->ioa_host_interrupt_mask_clr_reg =
+ mapped_pci_addr + chip_cfg->ioa_host_mask_clr;
+ pint_regs->global_interrupt_mask_reg =
+ mapped_pci_addr + chip_cfg->global_intr_mask;
+ };
+
+ pinstance->ioa_reset_attempts = 0;
+ init_waitqueue_head(&pinstance->reset_wait_q);
+
+ atomic_set(&pinstance->outstanding_cmds, 0);
+ atomic_set(&pinstance->expose_resources, 0);
+
+ INIT_LIST_HEAD(&pinstance->free_res_q);
+ INIT_LIST_HEAD(&pinstance->used_res_q);
+ INIT_LIST_HEAD(&pinstance->free_cmd_pool);
+ INIT_LIST_HEAD(&pinstance->pending_cmd_pool);
+
+ spin_lock_init(&pinstance->free_pool_lock);
+ spin_lock_init(&pinstance->pending_pool_lock);
+ spin_lock_init(&pinstance->resource_lock);
+ mutex_init(&pinstance->aen_queue_lock);
+
+ /* Work-queue (Shared) for deferred processing error handling */
+ INIT_WORK(&pinstance->worker_q, pmcraid_worker_function);
+
+ /* Initialize the default log_level */
+ pinstance->current_log_level = pmcraid_log_level;
+
+ /* Setup variables required for reset engine */
+ pinstance->ioa_state = IOA_STATE_UNKNOWN;
+ pinstance->reset_cmd = NULL;
+ return 0;
+}
+
+/**
+ * pmcraid_release_buffers - release per-adapter buffers allocated
+ *
+ * @pinstance: pointer to adapter soft state
+ *
+ * Return Value
+ * none
+ */
+static void pmcraid_release_buffers(struct pmcraid_instance *pinstance)
+{
+ pmcraid_release_config_buffers(pinstance);
+ pmcraid_release_control_blocks(pinstance, PMCRAID_MAX_CMD);
+ pmcraid_release_cmd_blocks(pinstance, PMCRAID_MAX_CMD);
+ pmcraid_release_host_rrqs(pinstance, pinstance->num_hrrq);
+
+}
+
+/**
+ * pmcraid_shutdown - shutdown adapter controller.
+ * @pdev: pci device struct
+ *
+ * Issues an adapter shutdown to the card waits for its completion
+ *
+ * Return value
+ * none
+ */
+static void pmcraid_shutdown(struct pci_dev *pdev)
+{
+ struct pmcraid_instance *pinstance = pci_get_drvdata(pdev);
+ pmcraid_reset_bringdown(pinstance);
+}
+
+
+/**
+ * pmcraid_get_minor - returns unused minor number from minor number bitmap
+ */
+static unsigned short pmcraid_get_minor(void)
+{
+ int minor;
+
+ minor = find_first_zero_bit(pmcraid_minor, sizeof(pmcraid_minor));
+ __set_bit(minor, pmcraid_minor);
+ return minor;
+}
+
+/**
+ * pmcraid_release_minor - releases given minor back to minor number bitmap
+ */
+static void pmcraid_release_minor(unsigned short minor)
+{
+ __clear_bit(minor, pmcraid_minor);
+}
+
+/**
+ * pmcraid_setup_chrdev - allocates a minor number and registers a char device
+ *
+ * @pinstance: pointer to adapter instance for which to register device
+ *
+ * Return value
+ * 0 in case of success, otherwise non-zero
+ */
+static int pmcraid_setup_chrdev(struct pmcraid_instance *pinstance)
+{
+ int minor;
+ int error;
+
+ minor = pmcraid_get_minor();
+ cdev_init(&pinstance->cdev, &pmcraid_fops);
+ pinstance->cdev.owner = THIS_MODULE;
+
+ error = cdev_add(&pinstance->cdev, MKDEV(pmcraid_major, minor), 1);
+
+ if (error)
+ pmcraid_release_minor(minor);
+ else
+ device_create(pmcraid_class, NULL, MKDEV(pmcraid_major, minor),
+ NULL, "pmcsas%u", minor);
+ return error;
+}
+
+/**
+ * pmcraid_release_chrdev - unregisters per-adapter management interface
+ *
+ * @pinstance: pointer to adapter instance structure
+ *
+ * Return value
+ * none
+ */
+static void pmcraid_release_chrdev(struct pmcraid_instance *pinstance)
+{
+ pmcraid_release_minor(MINOR(pinstance->cdev.dev));
+ device_destroy(pmcraid_class,
+ MKDEV(pmcraid_major, MINOR(pinstance->cdev.dev)));
+ cdev_del(&pinstance->cdev);
+}
+
+/**
+ * pmcraid_remove - IOA hot plug remove entry point
+ * @pdev: pci device struct
+ *
+ * Return value
+ * none
+ */
+static void __devexit pmcraid_remove(struct pci_dev *pdev)
+{
+ struct pmcraid_instance *pinstance = pci_get_drvdata(pdev);
+
+ /* remove the management interface (/dev file) for this device */
+ pmcraid_release_chrdev(pinstance);
+
+ /* remove host template from scsi midlayer */
+ scsi_remove_host(pinstance->host);
+
+ /* block requests from mid-layer */
+ scsi_block_requests(pinstance->host);
+
+ /* initiate shutdown adapter */
+ pmcraid_shutdown(pdev);
+
+ pmcraid_disable_interrupts(pinstance, ~0);
+ flush_scheduled_work();
+
+ pmcraid_kill_tasklets(pinstance);
+ pmcraid_unregister_interrupt_handler(pinstance);
+ pmcraid_release_buffers(pinstance);
+ iounmap(pinstance->mapped_dma_addr);
+ pci_release_regions(pdev);
+ scsi_host_put(pinstance->host);
+ pci_disable_device(pdev);
+
+ return;
+}
+
+#ifdef CONFIG_PM
+/**
+ * pmcraid_suspend - driver suspend entry point for power management
+ * @pdev: PCI device structure
+ * @state: PCI power state to suspend routine
+ *
+ * Return Value - 0 always
+ */
+static int pmcraid_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct pmcraid_instance *pinstance = pci_get_drvdata(pdev);
+
+ pmcraid_shutdown(pdev);
+ pmcraid_disable_interrupts(pinstance, ~0);
+ pmcraid_kill_tasklets(pinstance);
+ pci_set_drvdata(pinstance->pdev, pinstance);
+ pmcraid_unregister_interrupt_handler(pinstance);
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ return 0;
+}
+
+/**
+ * pmcraid_resume - driver resume entry point PCI power management
+ * @pdev: PCI device structure
+ *
+ * Return Value - 0 in case of success. Error code in case of any failure
+ */
+static int pmcraid_resume(struct pci_dev *pdev)
+{
+ struct pmcraid_instance *pinstance = pci_get_drvdata(pdev);
+ struct Scsi_Host *host = pinstance->host;
+ int rc;
+ int hrrqs;
+
+ pci_set_power_state(pdev, PCI_D0);
+ pci_enable_wake(pdev, PCI_D0, 0);
+ pci_restore_state(pdev);
+
+ rc = pci_enable_device(pdev);
+
+ if (rc) {
+ pmcraid_err("pmcraid: Enable device failed\n");
+ return rc;
+ }
+
+ pci_set_master(pdev);
+
+ if ((sizeof(dma_addr_t) == 4) ||
+ pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
+ rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+
+ if (rc == 0)
+ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+
+ if (rc != 0) {
+ dev_err(&pdev->dev, "Failed to set PCI DMA mask\n");
+ goto disable_device;
+ }
+
+ atomic_set(&pinstance->outstanding_cmds, 0);
+ hrrqs = pinstance->num_hrrq;
+ rc = pmcraid_register_interrupt_handler(pinstance);
+
+ if (rc) {
+ pmcraid_err("resume: couldn't register interrupt handlers\n");
+ rc = -ENODEV;
+ goto release_host;
+ }
+
+ pmcraid_init_tasklets(pinstance);
+ pmcraid_enable_interrupts(pinstance, PMCRAID_PCI_INTERRUPTS);
+
+ /* Start with hard reset sequence which brings up IOA to operational
+ * state as well as completes the reset sequence.
+ */
+ pinstance->ioa_hard_reset = 1;
+
+ /* Start IOA firmware initialization and bring card to Operational
+ * state.
+ */
+ if (pmcraid_reset_bringup(pinstance)) {
+ pmcraid_err("couldn't initialize IOA \n");
+ rc = -ENODEV;
+ goto release_tasklets;
+ }
+
+ return 0;
+
+release_tasklets:
+ pmcraid_kill_tasklets(pinstance);
+ pmcraid_unregister_interrupt_handler(pinstance);
+
+release_host:
+ scsi_host_put(host);
+
+disable_device:
+ pci_disable_device(pdev);
+
+ return rc;
+}
+
+#else
+
+#define pmcraid_suspend NULL
+#define pmcraid_resume NULL
+
+#endif /* CONFIG_PM */
+
+/**
+ * pmcraid_complete_ioa_reset - Called by either timer or tasklet during
+ * completion of the ioa reset
+ * @cmd: pointer to reset command block
+ */
+static void pmcraid_complete_ioa_reset(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ unsigned long flags;
+
+ spin_lock_irqsave(pinstance->host->host_lock, flags);
+ pmcraid_ioa_reset(cmd);
+ spin_unlock_irqrestore(pinstance->host->host_lock, flags);
+ scsi_unblock_requests(pinstance->host);
+ schedule_work(&pinstance->worker_q);
+}
+
+/**
+ * pmcraid_set_supported_devs - sends SET SUPPORTED DEVICES to IOAFP
+ *
+ * @cmd: pointer to pmcraid_cmd structure
+ *
+ * Return Value
+ * 0 for success or non-zero for failure cases
+ */
+static void pmcraid_set_supported_devs(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
+ void (*cmd_done) (struct pmcraid_cmd *) = pmcraid_complete_ioa_reset;
+
+ pmcraid_reinit_cmdblk(cmd);
+
+ ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
+ ioarcb->request_type = REQ_TYPE_IOACMD;
+ ioarcb->cdb[0] = PMCRAID_SET_SUPPORTED_DEVICES;
+ ioarcb->cdb[1] = ALL_DEVICES_SUPPORTED;
+
+ /* If this was called as part of resource table reinitialization due to
+ * lost CCN, it is enough to return the command block back to free pool
+ * as part of set_supported_devs completion function.
+ */
+ if (cmd->drv_inst->reinit_cfg_table) {
+ cmd->drv_inst->reinit_cfg_table = 0;
+ cmd->release = 1;
+ cmd_done = pmcraid_reinit_cfgtable_done;
+ }
+
+ /* we will be done with the reset sequence after set supported devices,
+ * setup the done function to return the command block back to free
+ * pool
+ */
+ pmcraid_send_cmd(cmd,
+ cmd_done,
+ PMCRAID_SET_SUP_DEV_TIMEOUT,
+ pmcraid_timeout_handler);
+ return;
+}
+
+/**
+ * pmcraid_init_res_table - Initialize the resource table
+ * @cmd: pointer to pmcraid command struct
+ *
+ * This function looks through the existing resource table, comparing
+ * it with the config table. This function will take care of old/new
+ * devices and schedule adding/removing them from the mid-layer
+ * as appropriate.
+ *
+ * Return value
+ * None
+ */
+static void pmcraid_init_res_table(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ struct pmcraid_resource_entry *res, *temp;
+ struct pmcraid_config_table_entry *cfgte;
+ unsigned long lock_flags;
+ int found, rc, i;
+ LIST_HEAD(old_res);
+
+ if (pinstance->cfg_table->flags & MICROCODE_UPDATE_REQUIRED)
+ dev_err(&pinstance->pdev->dev, "Require microcode download\n");
+
+ /* resource list is protected by pinstance->resource_lock.
+ * init_res_table can be called from probe (user-thread) or runtime
+ * reset (timer/tasklet)
+ */
+ spin_lock_irqsave(&pinstance->resource_lock, lock_flags);
+
+ list_for_each_entry_safe(res, temp, &pinstance->used_res_q, queue)
+ list_move_tail(&res->queue, &old_res);
+
+ for (i = 0; i < pinstance->cfg_table->num_entries; i++) {
+ cfgte = &pinstance->cfg_table->entries[i];
+
+ if (!pmcraid_expose_resource(cfgte))
+ continue;
+
+ found = 0;
+
+ /* If this entry was already detected and initialized */
+ list_for_each_entry_safe(res, temp, &old_res, queue) {
+
+ rc = memcmp(&res->cfg_entry.resource_address,
+ &cfgte->resource_address,
+ sizeof(cfgte->resource_address));
+ if (!rc) {
+ list_move_tail(&res->queue,
+ &pinstance->used_res_q);
+ found = 1;
+ break;
+ }
+ }
+
+ /* If this is new entry, initialize it and add it the queue */
+ if (!found) {
+
+ if (list_empty(&pinstance->free_res_q)) {
+ dev_err(&pinstance->pdev->dev,
+ "Too many devices attached\n");
+ break;
+ }
+
+ found = 1;
+ res = list_entry(pinstance->free_res_q.next,
+ struct pmcraid_resource_entry, queue);
+
+ res->scsi_dev = NULL;
+ res->change_detected = RES_CHANGE_ADD;
+ res->reset_progress = 0;
+ list_move_tail(&res->queue, &pinstance->used_res_q);
+ }
+
+ /* copy new configuration table entry details into driver
+ * maintained resource entry
+ */
+ if (found) {
+ memcpy(&res->cfg_entry, cfgte,
+ sizeof(struct pmcraid_config_table_entry));
+ pmcraid_info("New res type:%x, vset:%x, addr:%x:\n",
+ res->cfg_entry.resource_type,
+ res->cfg_entry.unique_flags1,
+ le32_to_cpu(res->cfg_entry.resource_address));
+ }
+ }
+
+ /* Detect any deleted entries, mark them for deletion from mid-layer */
+ list_for_each_entry_safe(res, temp, &old_res, queue) {
+
+ if (res->scsi_dev) {
+ res->change_detected = RES_CHANGE_DEL;
+ res->cfg_entry.resource_handle =
+ PMCRAID_INVALID_RES_HANDLE;
+ list_move_tail(&res->queue, &pinstance->used_res_q);
+ } else {
+ list_move_tail(&res->queue, &pinstance->free_res_q);
+ }
+ }
+
+ /* release the resource list lock */
+ spin_unlock_irqrestore(&pinstance->resource_lock, lock_flags);
+ pmcraid_set_supported_devs(cmd);
+}
+
+/**
+ * pmcraid_querycfg - Send a Query IOA Config to the adapter.
+ * @cmd: pointer pmcraid_cmd struct
+ *
+ * This function sends a Query IOA Configuration command to the adapter to
+ * retrieve the IOA configuration table.
+ *
+ * Return value:
+ * none
+ */
+static void pmcraid_querycfg(struct pmcraid_cmd *cmd)
+{
+ struct pmcraid_ioarcb *ioarcb = &cmd->ioa_cb->ioarcb;
+ struct pmcraid_ioadl_desc *ioadl = ioarcb->add_data.u.ioadl;
+ struct pmcraid_instance *pinstance = cmd->drv_inst;
+ int cfg_table_size = cpu_to_be32(sizeof(struct pmcraid_config_table));
+
+ ioarcb->request_type = REQ_TYPE_IOACMD;
+ ioarcb->resource_handle = cpu_to_le32(PMCRAID_IOA_RES_HANDLE);
+
+ ioarcb->cdb[0] = PMCRAID_QUERY_IOA_CONFIG;
+
+ /* firmware requires 4-byte length field, specified in B.E format */
+ memcpy(&(ioarcb->cdb[10]), &cfg_table_size, sizeof(cfg_table_size));
+
+ /* Since entire config table can be described by single IOADL, it can
+ * be part of IOARCB itself
+ */
+ ioarcb->ioadl_bus_addr = cpu_to_le64((cmd->ioa_cb_bus_addr) +
+ offsetof(struct pmcraid_ioarcb,
+ add_data.u.ioadl[0]));
+ ioarcb->ioadl_length = cpu_to_le32(sizeof(struct pmcraid_ioadl_desc));
+ ioarcb->ioarcb_bus_addr &= ~(0x1FULL);
+
+ ioarcb->request_flags0 |= NO_LINK_DESCS;
+ ioarcb->data_transfer_length =
+ cpu_to_le32(sizeof(struct pmcraid_config_table));
+
+ ioadl = &(ioarcb->add_data.u.ioadl[0]);
+ ioadl->flags = cpu_to_le32(IOADL_FLAGS_LAST_DESC);
+ ioadl->address = cpu_to_le64(pinstance->cfg_table_bus_addr);
+ ioadl->data_len = cpu_to_le32(sizeof(struct pmcraid_config_table));
+
+ pmcraid_send_cmd(cmd, pmcraid_init_res_table,
+ PMCRAID_INTERNAL_TIMEOUT, pmcraid_timeout_handler);
+}
+
+
+/**
+ * pmcraid_probe - PCI probe entry pointer for PMC MaxRaid controller driver
+ * @pdev: pointer to pci device structure
+ * @dev_id: pointer to device ids structure
+ *
+ * Return Value
+ * returns 0 if the device is claimed and successfully configured.
+ * returns non-zero error code in case of any failure
+ */
+static int __devinit pmcraid_probe(
+ struct pci_dev *pdev,
+ const struct pci_device_id *dev_id
+)
+{
+ struct pmcraid_instance *pinstance;
+ struct Scsi_Host *host;
+ void __iomem *mapped_pci_addr;
+ int rc = PCIBIOS_SUCCESSFUL;
+
+ if (atomic_read(&pmcraid_adapter_count) >= PMCRAID_MAX_ADAPTERS) {
+ pmcraid_err
+ ("maximum number(%d) of supported adapters reached\n",
+ atomic_read(&pmcraid_adapter_count));
+ return -ENOMEM;
+ }
+
+ atomic_inc(&pmcraid_adapter_count);
+ rc = pci_enable_device(pdev);
+
+ if (rc) {
+ dev_err(&pdev->dev, "Cannot enable adapter\n");
+ atomic_dec(&pmcraid_adapter_count);
+ return rc;
+ }
+
+ dev_info(&pdev->dev,
+ "Found new IOA(%x:%x), Total IOA count: %d\n",
+ pdev->vendor, pdev->device,
+ atomic_read(&pmcraid_adapter_count));
+
+ rc = pci_request_regions(pdev, PMCRAID_DRIVER_NAME);
+
+ if (rc < 0) {
+ dev_err(&pdev->dev,
+ "Couldn't register memory range of registers\n");
+ goto out_disable_device;
+ }
+
+ mapped_pci_addr = pci_iomap(pdev, 0, 0);
+
+ if (!mapped_pci_addr) {
+ dev_err(&pdev->dev, "Couldn't map PCI registers memory\n");
+ rc = -ENOMEM;
+ goto out_release_regions;
+ }
+
+ pci_set_master(pdev);
+
+ /* Firmware requires the system bus address of IOARCB to be within
+ * 32-bit addressable range though it has 64-bit IOARRIN register.
+ * However, firmware supports 64-bit streaming DMA buffers, whereas
+ * coherent buffers are to be 32-bit. Since pci_alloc_consistent always
+ * returns memory within 4GB (if not, change this logic), coherent
+ * buffers are within firmware acceptible address ranges.
+ */
+ if ((sizeof(dma_addr_t) == 4) ||
+ pci_set_dma_mask(pdev, DMA_BIT_MASK(64)))
+ rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+
+ /* firmware expects 32-bit DMA addresses for IOARRIN register; set 32
+ * bit mask for pci_alloc_consistent to return addresses within 4GB
+ */
+ if (rc == 0)
+ rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
+
+ if (rc != 0) {
+ dev_err(&pdev->dev, "Failed to set PCI DMA mask\n");
+ goto cleanup_nomem;
+ }
+
+ host = scsi_host_alloc(&pmcraid_host_template,
+ sizeof(struct pmcraid_instance));
+
+ if (!host) {
+ dev_err(&pdev->dev, "scsi_host_alloc failed!\n");
+ rc = -ENOMEM;
+ goto cleanup_nomem;
+ }
+
+ host->max_id = PMCRAID_MAX_NUM_TARGETS_PER_BUS;
+ host->max_lun = PMCRAID_MAX_NUM_LUNS_PER_TARGET;
+ host->unique_id = host->host_no;
+ host->max_channel = PMCRAID_MAX_BUS_TO_SCAN;
+ host->max_cmd_len = PMCRAID_MAX_CDB_LEN;
+
+ /* zero out entire instance structure */
+ pinstance = (struct pmcraid_instance *)host->hostdata;
+ memset(pinstance, 0, sizeof(*pinstance));
+
+ pinstance->chip_cfg =
+ (struct pmcraid_chip_details *)(dev_id->driver_data);
+
+ rc = pmcraid_init_instance(pdev, host, mapped_pci_addr);
+
+ if (rc < 0) {
+ dev_err(&pdev->dev, "failed to initialize adapter instance\n");
+ goto out_scsi_host_put;
+ }
+
+ pci_set_drvdata(pdev, pinstance);
+
+ /* Save PCI config-space for use following the reset */
+ rc = pci_save_state(pinstance->pdev);
+
+ if (rc != 0) {
+ dev_err(&pdev->dev, "Failed to save PCI config space\n");
+ goto out_scsi_host_put;
+ }
+
+ pmcraid_disable_interrupts(pinstance, ~0);
+
+ rc = pmcraid_register_interrupt_handler(pinstance);
+
+ if (rc) {
+ pmcraid_err("couldn't register interrupt handler\n");
+ goto out_scsi_host_put;
+ }
+
+ pmcraid_init_tasklets(pinstance);
+
+ /* allocate verious buffers used by LLD.*/
+ rc = pmcraid_init_buffers(pinstance);
+
+ if (rc) {
+ pmcraid_err("couldn't allocate memory blocks\n");
+ goto out_unregister_isr;
+ }
+
+ /* check the reset type required */
+ pmcraid_reset_type(pinstance);
+
+ pmcraid_enable_interrupts(pinstance, PMCRAID_PCI_INTERRUPTS);
+
+ /* Start IOA firmware initialization and bring card to Operational
+ * state.
+ */
+ pmcraid_info("starting IOA initialization sequence\n");
+ if (pmcraid_reset_bringup(pinstance)) {
+ pmcraid_err("couldn't initialize IOA \n");
+ rc = 1;
+ goto out_release_bufs;
+ }
+
+ /* Add adapter instance into mid-layer list */
+ rc = scsi_add_host(pinstance->host, &pdev->dev);
+ if (rc != 0) {
+ pmcraid_err("couldn't add host into mid-layer: %d\n", rc);
+ goto out_release_bufs;
+ }
+
+ scsi_scan_host(pinstance->host);
+
+ rc = pmcraid_setup_chrdev(pinstance);
+
+ if (rc != 0) {
+ pmcraid_err("couldn't create mgmt interface, error: %x\n",
+ rc);
+ goto out_remove_host;
+ }
+
+ /* Schedule worker thread to handle CCN and take care of adding and
+ * removing devices to OS
+ */
+ atomic_set(&pinstance->expose_resources, 1);
+ schedule_work(&pinstance->worker_q);
+ return rc;
+
+out_remove_host:
+ scsi_remove_host(host);
+
+out_release_bufs:
+ pmcraid_release_buffers(pinstance);
+
+out_unregister_isr:
+ pmcraid_kill_tasklets(pinstance);
+ pmcraid_unregister_interrupt_handler(pinstance);
+
+out_scsi_host_put:
+ scsi_host_put(host);
+
+cleanup_nomem:
+ iounmap(mapped_pci_addr);
+
+out_release_regions:
+ pci_release_regions(pdev);
+
+out_disable_device:
+ atomic_dec(&pmcraid_adapter_count);
+ pci_set_drvdata(pdev, NULL);
+ pci_disable_device(pdev);
+ return -ENODEV;
+}
+
+/*
+ * PCI driver structure of pcmraid driver
+ */
+static struct pci_driver pmcraid_driver = {
+ .name = PMCRAID_DRIVER_NAME,
+ .id_table = pmcraid_pci_table,
+ .probe = pmcraid_probe,
+ .remove = pmcraid_remove,
+ .suspend = pmcraid_suspend,
+ .resume = pmcraid_resume,
+ .shutdown = pmcraid_shutdown
+};
+
+
+/**
+ * pmcraid_init - module load entry point
+ */
+static int __init pmcraid_init(void)
+{
+ dev_t dev;
+ int error;
+
+ pmcraid_info("%s Device Driver version: %s %s\n",
+ PMCRAID_DRIVER_NAME,
+ PMCRAID_DRIVER_VERSION, PMCRAID_DRIVER_DATE);
+
+ error = alloc_chrdev_region(&dev, 0,
+ PMCRAID_MAX_ADAPTERS,
+ PMCRAID_DEVFILE);
+
+ if (error) {
+ pmcraid_err("failed to get a major number for adapters\n");
+ goto out_init;
+ }
+
+ pmcraid_major = MAJOR(dev);
+ pmcraid_class = class_create(THIS_MODULE, PMCRAID_DEVFILE);
+
+ if (IS_ERR(pmcraid_class)) {
+ error = PTR_ERR(pmcraid_class);
+ pmcraid_err("failed to register with with sysfs, error = %x\n",
+ error);
+ goto out_unreg_chrdev;
+ }
+
+
+ error = pmcraid_netlink_init();
+
+ if (error)
+ goto out_unreg_chrdev;
+
+ error = pci_register_driver(&pmcraid_driver);
+
+ if (error == 0)
+ goto out_init;
+
+ pmcraid_err("failed to register pmcraid driver, error = %x\n",
+ error);
+ class_destroy(pmcraid_class);
+ pmcraid_netlink_release();
+
+out_unreg_chrdev:
+ unregister_chrdev_region(MKDEV(pmcraid_major, 0), PMCRAID_MAX_ADAPTERS);
+out_init:
+ return error;
+}
+
+/**
+ * pmcraid_exit - module unload entry point
+ */
+static void __exit pmcraid_exit(void)
+{
+ pmcraid_netlink_release();
+ class_destroy(pmcraid_class);
+ unregister_chrdev_region(MKDEV(pmcraid_major, 0),
+ PMCRAID_MAX_ADAPTERS);
+ pci_unregister_driver(&pmcraid_driver);
+}
+
+module_init(pmcraid_init);
+module_exit(pmcraid_exit);
diff --git a/drivers/scsi/pmcraid.h b/drivers/scsi/pmcraid.h
new file mode 100644
index 00000000000..614b3a764fe
--- /dev/null
+++ b/drivers/scsi/pmcraid.h
@@ -0,0 +1,1029 @@
+/*
+ * pmcraid.h -- PMC Sierra MaxRAID controller driver header file
+ *
+ * Copyright (C) 2008, 2009 PMC Sierra Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _PMCRAID_H
+#define _PMCRAID_H
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/list.h>
+#include <scsi/scsi.h>
+#include <linux/kref.h>
+#include <scsi/scsi_cmnd.h>
+#include <linux/cdev.h>
+#include <net/netlink.h>
+#include <net/genetlink.h>
+#include <linux/connector.h>
+/*
+ * Driver name : string representing the driver name
+ * Device file : /dev file to be used for management interfaces
+ * Driver version: version string in major_version.minor_version.patch format
+ * Driver date : date information in "Mon dd yyyy" format
+ */
+#define PMCRAID_DRIVER_NAME "PMC MaxRAID"
+#define PMCRAID_DEVFILE "pmcsas"
+#define PMCRAID_DRIVER_VERSION "1.0.2"
+#define PMCRAID_DRIVER_DATE __DATE__
+
+/* Maximum number of adapters supported by current version of the driver */
+#define PMCRAID_MAX_ADAPTERS 1024
+
+/* Bit definitions as per firmware, bit position [0][1][2].....[31] */
+#define PMC_BIT8(n) (1 << (7-n))
+#define PMC_BIT16(n) (1 << (15-n))
+#define PMC_BIT32(n) (1 << (31-n))
+
+/* PMC PCI vendor ID and device ID values */
+#define PCI_VENDOR_ID_PMC 0x11F8
+#define PCI_DEVICE_ID_PMC_MAXRAID 0x5220
+
+/*
+ * MAX_CMD : maximum commands that can be outstanding with IOA
+ * MAX_IO_CMD : command blocks available for IO commands
+ * MAX_HCAM_CMD : command blocks avaibale for HCAMS
+ * MAX_INTERNAL_CMD : command blocks avaible for internal commands like reset
+ */
+#define PMCRAID_MAX_CMD 1024
+#define PMCRAID_MAX_IO_CMD 1020
+#define PMCRAID_MAX_HCAM_CMD 2
+#define PMCRAID_MAX_INTERNAL_CMD 2
+
+/* MAX_IOADLS : max number of scatter-gather lists supported by IOA
+ * IOADLS_INTERNAL : number of ioadls included as part of IOARCB.
+ * IOADLS_EXTERNAL : number of ioadls allocated external to IOARCB
+ */
+#define PMCRAID_IOADLS_INTERNAL 27
+#define PMCRAID_IOADLS_EXTERNAL 37
+#define PMCRAID_MAX_IOADLS PMCRAID_IOADLS_INTERNAL
+
+/* HRRQ_ENTRY_SIZE : size of hrrq buffer
+ * IOARCB_ALIGNMENT : alignment required for IOARCB
+ * IOADL_ALIGNMENT : alignment requirement for IOADLs
+ * MSIX_VECTORS : number of MSIX vectors supported
+ */
+#define HRRQ_ENTRY_SIZE sizeof(__le32)
+#define PMCRAID_IOARCB_ALIGNMENT 32
+#define PMCRAID_IOADL_ALIGNMENT 16
+#define PMCRAID_IOASA_ALIGNMENT 4
+#define PMCRAID_NUM_MSIX_VECTORS 1
+
+/* various other limits */
+#define PMCRAID_VENDOR_ID_LEN 8
+#define PMCRAID_PRODUCT_ID_LEN 16
+#define PMCRAID_SERIAL_NUM_LEN 8
+#define PMCRAID_LUN_LEN 8
+#define PMCRAID_MAX_CDB_LEN 16
+#define PMCRAID_DEVICE_ID_LEN 8
+#define PMCRAID_SENSE_DATA_LEN 256
+#define PMCRAID_ADD_CMD_PARAM_LEN 48
+
+#define PMCRAID_MAX_BUS_TO_SCAN 1
+#define PMCRAID_MAX_NUM_TARGETS_PER_BUS 256
+#define PMCRAID_MAX_NUM_LUNS_PER_TARGET 8
+
+/* IOA bus/target/lun number of IOA resources */
+#define PMCRAID_IOA_BUS_ID 0xfe
+#define PMCRAID_IOA_TARGET_ID 0xff
+#define PMCRAID_IOA_LUN_ID 0xff
+#define PMCRAID_VSET_BUS_ID 0x1
+#define PMCRAID_VSET_LUN_ID 0x0
+#define PMCRAID_PHYS_BUS_ID 0x0
+#define PMCRAID_VIRTUAL_ENCL_BUS_ID 0x8
+#define PMCRAID_MAX_VSET_TARGETS 240
+#define PMCRAID_MAX_VSET_LUNS_PER_TARGET 8
+
+#define PMCRAID_IOA_MAX_SECTORS 32767
+#define PMCRAID_VSET_MAX_SECTORS 512
+#define PMCRAID_MAX_CMD_PER_LUN 254
+
+/* Number of configuration table entries (resources) */
+#define PMCRAID_MAX_NUM_OF_VSETS 240
+
+/* Todo : Check max limit for Phase 1 */
+#define PMCRAID_MAX_NUM_OF_PHY_DEVS 256
+
+/* MAX_NUM_OF_DEVS includes 1 FP, 1 Dummy Enclosure device */
+#define PMCRAID_MAX_NUM_OF_DEVS \
+ (PMCRAID_MAX_NUM_OF_VSETS + PMCRAID_MAX_NUM_OF_PHY_DEVS + 2)
+
+#define PMCRAID_MAX_RESOURCES PMCRAID_MAX_NUM_OF_DEVS
+
+/* Adapter Commands used by driver */
+#define PMCRAID_QUERY_RESOURCE_STATE 0xC2
+#define PMCRAID_RESET_DEVICE 0xC3
+/* options to select reset target */
+#define ENABLE_RESET_MODIFIER 0x80
+#define RESET_DEVICE_LUN 0x40
+#define RESET_DEVICE_TARGET 0x20
+#define RESET_DEVICE_BUS 0x10
+
+#define PMCRAID_IDENTIFY_HRRQ 0xC4
+#define PMCRAID_QUERY_IOA_CONFIG 0xC5
+#define PMCRAID_QUERY_CMD_STATUS 0xCB
+#define PMCRAID_ABORT_CMD 0xC7
+
+/* CANCEL ALL command, provides option for setting SYNC_COMPLETE
+ * on the target resources for which commands got cancelled
+ */
+#define PMCRAID_CANCEL_ALL_REQUESTS 0xCE
+#define PMCRAID_SYNC_COMPLETE_AFTER_CANCEL PMC_BIT8(0)
+
+/* HCAM command and types of HCAM supported by IOA */
+#define PMCRAID_HOST_CONTROLLED_ASYNC 0xCF
+#define PMCRAID_HCAM_CODE_CONFIG_CHANGE 0x01
+#define PMCRAID_HCAM_CODE_LOG_DATA 0x02
+
+/* IOA shutdown command and various shutdown types */
+#define PMCRAID_IOA_SHUTDOWN 0xF7
+#define PMCRAID_SHUTDOWN_NORMAL 0x00
+#define PMCRAID_SHUTDOWN_PREPARE_FOR_NORMAL 0x40
+#define PMCRAID_SHUTDOWN_NONE 0x100
+#define PMCRAID_SHUTDOWN_ABBREV 0x80
+
+/* SET SUPPORTED DEVICES command and the option to select all the
+ * devices to be supported
+ */
+#define PMCRAID_SET_SUPPORTED_DEVICES 0xFB
+#define ALL_DEVICES_SUPPORTED PMC_BIT8(0)
+
+/* This option is used with SCSI WRITE_BUFFER command */
+#define PMCRAID_WR_BUF_DOWNLOAD_AND_SAVE 0x05
+
+/* IOASC Codes used by driver */
+#define PMCRAID_IOASC_SENSE_MASK 0xFFFFFF00
+#define PMCRAID_IOASC_SENSE_KEY(ioasc) ((ioasc) >> 24)
+#define PMCRAID_IOASC_SENSE_CODE(ioasc) (((ioasc) & 0x00ff0000) >> 16)
+#define PMCRAID_IOASC_SENSE_QUAL(ioasc) (((ioasc) & 0x0000ff00) >> 8)
+#define PMCRAID_IOASC_SENSE_STATUS(ioasc) ((ioasc) & 0x000000ff)
+
+#define PMCRAID_IOASC_GOOD_COMPLETION 0x00000000
+#define PMCRAID_IOASC_NR_INIT_CMD_REQUIRED 0x02040200
+#define PMCRAID_IOASC_NR_IOA_RESET_REQUIRED 0x02048000
+#define PMCRAID_IOASC_NR_SYNC_REQUIRED 0x023F0000
+#define PMCRAID_IOASC_ME_READ_ERROR_NO_REALLOC 0x03110C00
+#define PMCRAID_IOASC_HW_CANNOT_COMMUNICATE 0x04050000
+#define PMCRAID_IOASC_HW_DEVICE_TIMEOUT 0x04080100
+#define PMCRAID_IOASC_HW_DEVICE_BUS_STATUS_ERROR 0x04448500
+#define PMCRAID_IOASC_HW_IOA_RESET_REQUIRED 0x04448600
+#define PMCRAID_IOASC_IR_INVALID_RESOURCE_HANDLE 0x05250000
+#define PMCRAID_IOASC_AC_TERMINATED_BY_HOST 0x0B5A0000
+#define PMCRAID_IOASC_UA_BUS_WAS_RESET 0x06290000
+#define PMCRAID_IOASC_UA_BUS_WAS_RESET_BY_OTHER 0x06298000
+
+/* Driver defined IOASCs */
+#define PMCRAID_IOASC_IOA_WAS_RESET 0x10000001
+#define PMCRAID_IOASC_PCI_ACCESS_ERROR 0x10000002
+
+/* Various timeout values (in milliseconds) used. If any of these are chip
+ * specific, move them to pmcraid_chip_details structure.
+ */
+#define PMCRAID_PCI_DEASSERT_TIMEOUT 2000
+#define PMCRAID_BIST_TIMEOUT 2000
+#define PMCRAID_AENWAIT_TIMEOUT 5000
+#define PMCRAID_TRANSOP_TIMEOUT 60000
+
+#define PMCRAID_RESET_TIMEOUT (2 * HZ)
+#define PMCRAID_CHECK_FOR_RESET_TIMEOUT ((HZ / 10))
+#define PMCRAID_VSET_IO_TIMEOUT (60 * HZ)
+#define PMCRAID_INTERNAL_TIMEOUT (60 * HZ)
+#define PMCRAID_SHUTDOWN_TIMEOUT (150 * HZ)
+#define PMCRAID_RESET_BUS_TIMEOUT (60 * HZ)
+#define PMCRAID_RESET_HOST_TIMEOUT (150 * HZ)
+#define PMCRAID_REQUEST_SENSE_TIMEOUT (30 * HZ)
+#define PMCRAID_SET_SUP_DEV_TIMEOUT (2 * 60 * HZ)
+
+/* structure to represent a scatter-gather element (IOADL descriptor) */
+struct pmcraid_ioadl_desc {
+ __le64 address;
+ __le32 data_len;
+ __u8 reserved[3];
+ __u8 flags;
+} __attribute__((packed, aligned(PMCRAID_IOADL_ALIGNMENT)));
+
+/* pmcraid_ioadl_desc.flags values */
+#define IOADL_FLAGS_CHAINED PMC_BIT8(0)
+#define IOADL_FLAGS_LAST_DESC PMC_BIT8(1)
+#define IOADL_FLAGS_READ_LAST PMC_BIT8(1)
+#define IOADL_FLAGS_WRITE_LAST PMC_BIT8(1)
+
+
+/* additional IOARCB data which can be CDB or additional request parameters
+ * or list of IOADLs. Firmware supports max of 512 bytes for IOARCB, hence then
+ * number of IOADLs are limted to 27. In case they are more than 27, they will
+ * be used in chained form
+ */
+struct pmcraid_ioarcb_add_data {
+ union {
+ struct pmcraid_ioadl_desc ioadl[PMCRAID_IOADLS_INTERNAL];
+ __u8 add_cmd_params[PMCRAID_ADD_CMD_PARAM_LEN];
+ } u;
+};
+
+/*
+ * IOA Request Control Block
+ */
+struct pmcraid_ioarcb {
+ __le64 ioarcb_bus_addr;
+ __le32 resource_handle;
+ __le32 response_handle;
+ __le64 ioadl_bus_addr;
+ __le32 ioadl_length;
+ __le32 data_transfer_length;
+ __le64 ioasa_bus_addr;
+ __le16 ioasa_len;
+ __le16 cmd_timeout;
+ __le16 add_cmd_param_offset;
+ __le16 add_cmd_param_length;
+ __le32 reserved1[2];
+ __le32 reserved2;
+ __u8 request_type;
+ __u8 request_flags0;
+ __u8 request_flags1;
+ __u8 hrrq_id;
+ __u8 cdb[PMCRAID_MAX_CDB_LEN];
+ struct pmcraid_ioarcb_add_data add_data;
+} __attribute__((packed, aligned(PMCRAID_IOARCB_ALIGNMENT)));
+
+/* well known resource handle values */
+#define PMCRAID_IOA_RES_HANDLE 0xffffffff
+#define PMCRAID_INVALID_RES_HANDLE 0
+
+/* pmcraid_ioarcb.request_type values */
+#define REQ_TYPE_SCSI 0x00
+#define REQ_TYPE_IOACMD 0x01
+#define REQ_TYPE_HCAM 0x02
+
+/* pmcraid_ioarcb.flags0 values */
+#define TRANSFER_DIR_WRITE PMC_BIT8(0)
+#define INHIBIT_UL_CHECK PMC_BIT8(2)
+#define SYNC_OVERRIDE PMC_BIT8(3)
+#define SYNC_COMPLETE PMC_BIT8(4)
+#define NO_LINK_DESCS PMC_BIT8(5)
+
+/* pmcraid_ioarcb.flags1 values */
+#define DELAY_AFTER_RESET PMC_BIT8(0)
+#define TASK_TAG_SIMPLE 0x10
+#define TASK_TAG_ORDERED 0x20
+#define TASK_TAG_QUEUE_HEAD 0x30
+
+/* toggle bit offset in response handle */
+#define HRRQ_TOGGLE_BIT 0x01
+#define HRRQ_RESPONSE_BIT 0x02
+
+/* IOA Status Area */
+struct pmcraid_ioasa_vset {
+ __le32 failing_lba_hi;
+ __le32 failing_lba_lo;
+ __le32 reserved;
+} __attribute__((packed, aligned(4)));
+
+struct pmcraid_ioasa {
+ __le32 ioasc;
+ __le16 returned_status_length;
+ __le16 available_status_length;
+ __le32 residual_data_length;
+ __le32 ilid;
+ __le32 fd_ioasc;
+ __le32 fd_res_address;
+ __le32 fd_res_handle;
+ __le32 reserved;
+
+ /* resource specific sense information */
+ union {
+ struct pmcraid_ioasa_vset vset;
+ } u;
+
+ /* IOA autosense data */
+ __le16 auto_sense_length;
+ __le16 error_data_length;
+ __u8 sense_data[PMCRAID_SENSE_DATA_LEN];
+} __attribute__((packed, aligned(4)));
+
+#define PMCRAID_DRIVER_ILID 0xffffffff
+
+/* Config Table Entry per Resource */
+struct pmcraid_config_table_entry {
+ __u8 resource_type;
+ __u8 bus_protocol;
+ __le16 array_id;
+ __u8 common_flags0;
+ __u8 common_flags1;
+ __u8 unique_flags0;
+ __u8 unique_flags1; /*also used as vset target_id */
+ __le32 resource_handle;
+ __le32 resource_address;
+ __u8 device_id[PMCRAID_DEVICE_ID_LEN];
+ __u8 lun[PMCRAID_LUN_LEN];
+} __attribute__((packed, aligned(4)));
+
+/* resource types (config_table_entry.resource_type values) */
+#define RES_TYPE_AF_DASD 0x00
+#define RES_TYPE_GSCSI 0x01
+#define RES_TYPE_VSET 0x02
+#define RES_TYPE_IOA_FP 0xFF
+
+#define RES_IS_IOA(res) ((res).resource_type == RES_TYPE_IOA_FP)
+#define RES_IS_GSCSI(res) ((res).resource_type == RES_TYPE_GSCSI)
+#define RES_IS_VSET(res) ((res).resource_type == RES_TYPE_VSET)
+#define RES_IS_AFDASD(res) ((res).resource_type == RES_TYPE_AF_DASD)
+
+/* bus_protocol values used by driver */
+#define RES_TYPE_VENCLOSURE 0x8
+
+/* config_table_entry.common_flags0 */
+#define MULTIPATH_RESOURCE PMC_BIT32(0)
+
+/* unique_flags1 */
+#define IMPORT_MODE_MANUAL PMC_BIT8(0)
+
+/* well known resource handle values */
+#define RES_HANDLE_IOA 0xFFFFFFFF
+#define RES_HANDLE_NONE 0x00000000
+
+/* well known resource address values */
+#define RES_ADDRESS_IOAFP 0xFEFFFFFF
+#define RES_ADDRESS_INVALID 0xFFFFFFFF
+
+/* BUS/TARGET/LUN values from resource_addrr */
+#define RES_BUS(res_addr) (le32_to_cpu(res_addr) & 0xFF)
+#define RES_TARGET(res_addr) ((le32_to_cpu(res_addr) >> 16) & 0xFF)
+#define RES_LUN(res_addr) 0x0
+
+/* configuration table structure */
+struct pmcraid_config_table {
+ __le16 num_entries;
+ __u8 table_format;
+ __u8 reserved1;
+ __u8 flags;
+ __u8 reserved2[11];
+ struct pmcraid_config_table_entry entries[PMCRAID_MAX_RESOURCES];
+} __attribute__((packed, aligned(4)));
+
+/* config_table.flags value */
+#define MICROCODE_UPDATE_REQUIRED PMC_BIT32(0)
+
+/*
+ * HCAM format
+ */
+#define PMCRAID_HOSTRCB_LDNSIZE 4056
+
+/* Error log notification format */
+struct pmcraid_hostrcb_error {
+ __le32 fd_ioasc;
+ __le32 fd_ra;
+ __le32 fd_rh;
+ __le32 prc;
+ union {
+ __u8 data[PMCRAID_HOSTRCB_LDNSIZE];
+ } u;
+} __attribute__ ((packed, aligned(4)));
+
+struct pmcraid_hcam_hdr {
+ __u8 op_code;
+ __u8 notification_type;
+ __u8 notification_lost;
+ __u8 flags;
+ __u8 overlay_id;
+ __u8 reserved1[3];
+ __le32 ilid;
+ __le32 timestamp1;
+ __le32 timestamp2;
+ __le32 data_len;
+} __attribute__((packed, aligned(4)));
+
+#define PMCRAID_AEN_GROUP 0x3
+
+struct pmcraid_hcam_ccn {
+ struct pmcraid_hcam_hdr header;
+ struct pmcraid_config_table_entry cfg_entry;
+} __attribute__((packed, aligned(4)));
+
+struct pmcraid_hcam_ldn {
+ struct pmcraid_hcam_hdr header;
+ struct pmcraid_hostrcb_error error_log;
+} __attribute__((packed, aligned(4)));
+
+/* pmcraid_hcam.op_code values */
+#define HOSTRCB_TYPE_CCN 0xE1
+#define HOSTRCB_TYPE_LDN 0xE2
+
+/* pmcraid_hcam.notification_type values */
+#define NOTIFICATION_TYPE_ENTRY_CHANGED 0x0
+#define NOTIFICATION_TYPE_ENTRY_NEW 0x1
+#define NOTIFICATION_TYPE_ENTRY_DELETED 0x2
+#define NOTIFICATION_TYPE_ERROR_LOG 0x10
+#define NOTIFICATION_TYPE_INFORMATION_LOG 0x11
+
+#define HOSTRCB_NOTIFICATIONS_LOST PMC_BIT8(0)
+
+/* pmcraid_hcam.flags values */
+#define HOSTRCB_INTERNAL_OP_ERROR PMC_BIT8(0)
+#define HOSTRCB_ERROR_RESPONSE_SENT PMC_BIT8(1)
+
+/* pmcraid_hcam.overlay_id values */
+#define HOSTRCB_OVERLAY_ID_08 0x08
+#define HOSTRCB_OVERLAY_ID_09 0x09
+#define HOSTRCB_OVERLAY_ID_11 0x11
+#define HOSTRCB_OVERLAY_ID_12 0x12
+#define HOSTRCB_OVERLAY_ID_13 0x13
+#define HOSTRCB_OVERLAY_ID_14 0x14
+#define HOSTRCB_OVERLAY_ID_16 0x16
+#define HOSTRCB_OVERLAY_ID_17 0x17
+#define HOSTRCB_OVERLAY_ID_20 0x20
+#define HOSTRCB_OVERLAY_ID_FF 0xFF
+
+/* Implementation specific card details */
+struct pmcraid_chip_details {
+ /* hardware register offsets */
+ unsigned long ioastatus;
+ unsigned long ioarrin;
+ unsigned long mailbox;
+ unsigned long global_intr_mask;
+ unsigned long ioa_host_intr;
+ unsigned long ioa_host_intr_clr;
+ unsigned long ioa_host_mask;
+ unsigned long ioa_host_mask_clr;
+ unsigned long host_ioa_intr;
+ unsigned long host_ioa_intr_clr;
+
+ /* timeout used during transitional to operational state */
+ unsigned long transop_timeout;
+};
+
+/* IOA to HOST doorbells (interrupts) */
+#define INTRS_TRANSITION_TO_OPERATIONAL PMC_BIT32(0)
+#define INTRS_IOARCB_TRANSFER_FAILED PMC_BIT32(3)
+#define INTRS_IOA_UNIT_CHECK PMC_BIT32(4)
+#define INTRS_NO_HRRQ_FOR_CMD_RESPONSE PMC_BIT32(5)
+#define INTRS_CRITICAL_OP_IN_PROGRESS PMC_BIT32(6)
+#define INTRS_IO_DEBUG_ACK PMC_BIT32(7)
+#define INTRS_IOARRIN_LOST PMC_BIT32(27)
+#define INTRS_SYSTEM_BUS_MMIO_ERROR PMC_BIT32(28)
+#define INTRS_IOA_PROCESSOR_ERROR PMC_BIT32(29)
+#define INTRS_HRRQ_VALID PMC_BIT32(30)
+#define INTRS_OPERATIONAL_STATUS PMC_BIT32(0)
+
+/* Host to IOA Doorbells */
+#define DOORBELL_RUNTIME_RESET PMC_BIT32(1)
+#define DOORBELL_IOA_RESET_ALERT PMC_BIT32(7)
+#define DOORBELL_IOA_DEBUG_ALERT PMC_BIT32(9)
+#define DOORBELL_ENABLE_DESTRUCTIVE_DIAGS PMC_BIT32(8)
+#define DOORBELL_IOA_START_BIST PMC_BIT32(23)
+#define DOORBELL_RESET_IOA PMC_BIT32(31)
+
+/* Global interrupt mask register value */
+#define GLOBAL_INTERRUPT_MASK 0x4ULL
+
+#define PMCRAID_ERROR_INTERRUPTS (INTRS_IOARCB_TRANSFER_FAILED | \
+ INTRS_IOA_UNIT_CHECK | \
+ INTRS_NO_HRRQ_FOR_CMD_RESPONSE | \
+ INTRS_IOARRIN_LOST | \
+ INTRS_SYSTEM_BUS_MMIO_ERROR | \
+ INTRS_IOA_PROCESSOR_ERROR)
+
+#define PMCRAID_PCI_INTERRUPTS (PMCRAID_ERROR_INTERRUPTS | \
+ INTRS_HRRQ_VALID | \
+ INTRS_CRITICAL_OP_IN_PROGRESS |\
+ INTRS_TRANSITION_TO_OPERATIONAL)
+
+/* control_block, associated with each of the commands contains IOARCB, IOADLs
+ * memory for IOASA. Additional 3 * 16 bytes are allocated in order to support
+ * additional request parameters (of max size 48) any command.
+ */
+struct pmcraid_control_block {
+ struct pmcraid_ioarcb ioarcb;
+ struct pmcraid_ioadl_desc ioadl[PMCRAID_IOADLS_EXTERNAL + 3];
+ struct pmcraid_ioasa ioasa;
+} __attribute__ ((packed, aligned(PMCRAID_IOARCB_ALIGNMENT)));
+
+/* pmcraid_sglist - Scatter-gather list allocated for passthrough ioctls
+ */
+struct pmcraid_sglist {
+ u32 order;
+ u32 num_sg;
+ u32 num_dma_sg;
+ u32 buffer_len;
+ struct scatterlist scatterlist[1];
+};
+
+/* pmcraid_cmd - LLD representation of SCSI command */
+struct pmcraid_cmd {
+
+ /* Ptr and bus address of DMA.able control block for this command */
+ struct pmcraid_control_block *ioa_cb;
+ dma_addr_t ioa_cb_bus_addr;
+
+ /* sense buffer for REQUEST SENSE command if firmware is not sending
+ * auto sense data
+ */
+ dma_addr_t sense_buffer_dma;
+ dma_addr_t dma_handle;
+ u8 *sense_buffer;
+
+ /* pointer to mid layer structure of SCSI commands */
+ struct scsi_cmnd *scsi_cmd;
+
+ struct list_head free_list;
+ struct completion wait_for_completion;
+ struct timer_list timer; /* needed for internal commands */
+ u32 timeout; /* current timeout value */
+ u32 index; /* index into the command list */
+ u8 completion_req; /* for handling internal commands */
+ u8 release; /* for handling completions */
+
+ void (*cmd_done) (struct pmcraid_cmd *);
+ struct pmcraid_instance *drv_inst;
+
+ struct pmcraid_sglist *sglist; /* used for passthrough IOCTLs */
+
+ /* scratch used during reset sequence */
+ union {
+ unsigned long time_left;
+ struct pmcraid_resource_entry *res;
+ } u;
+};
+
+/*
+ * Interrupt registers of IOA
+ */
+struct pmcraid_interrupts {
+ void __iomem *ioa_host_interrupt_reg;
+ void __iomem *ioa_host_interrupt_clr_reg;
+ void __iomem *ioa_host_interrupt_mask_reg;
+ void __iomem *ioa_host_interrupt_mask_clr_reg;
+ void __iomem *global_interrupt_mask_reg;
+ void __iomem *host_ioa_interrupt_reg;
+ void __iomem *host_ioa_interrupt_clr_reg;
+};
+
+/* ISR parameters LLD allocates (one for each MSI-X if enabled) vectors */
+struct pmcraid_isr_param {
+ u8 hrrq_id; /* hrrq entry index */
+ u16 vector; /* allocated msi-x vector */
+ struct pmcraid_instance *drv_inst;
+};
+
+/* AEN message header sent as part of event data to applications */
+struct pmcraid_aen_msg {
+ u32 hostno;
+ u32 length;
+ u8 reserved[8];
+ u8 data[0];
+};
+
+struct pmcraid_hostrcb {
+ struct pmcraid_instance *drv_inst;
+ struct pmcraid_aen_msg *msg;
+ struct pmcraid_hcam_hdr *hcam; /* pointer to hcam buffer */
+ struct pmcraid_cmd *cmd; /* pointer to command block used */
+ dma_addr_t baddr; /* system address of hcam buffer */
+ atomic_t ignore; /* process HCAM response ? */
+};
+
+#define PMCRAID_AEN_HDR_SIZE sizeof(struct pmcraid_aen_msg)
+
+
+
+/*
+ * Per adapter structure maintained by LLD
+ */
+struct pmcraid_instance {
+ /* Array of allowed-to-be-exposed resources, initialized from
+ * Configutation Table, later updated with CCNs
+ */
+ struct pmcraid_resource_entry *res_entries;
+
+ struct list_head free_res_q; /* res_entries lists for easy lookup */
+ struct list_head used_res_q; /* List of to be exposed resources */
+ spinlock_t resource_lock; /* spinlock to protect resource list */
+
+ void __iomem *mapped_dma_addr;
+ void __iomem *ioa_status; /* Iomapped IOA status register */
+ void __iomem *mailbox; /* Iomapped mailbox register */
+ void __iomem *ioarrin; /* IOmapped IOARR IN register */
+
+ struct pmcraid_interrupts int_regs;
+ struct pmcraid_chip_details *chip_cfg;
+
+ /* HostRCBs needed for HCAM */
+ struct pmcraid_hostrcb ldn;
+ struct pmcraid_hostrcb ccn;
+
+
+ /* Bus address of start of HRRQ */
+ dma_addr_t hrrq_start_bus_addr[PMCRAID_NUM_MSIX_VECTORS];
+
+ /* Pointer to 1st entry of HRRQ */
+ __be32 *hrrq_start[PMCRAID_NUM_MSIX_VECTORS];
+
+ /* Pointer to last entry of HRRQ */
+ __be32 *hrrq_end[PMCRAID_NUM_MSIX_VECTORS];
+
+ /* Pointer to current pointer of hrrq */
+ __be32 *hrrq_curr[PMCRAID_NUM_MSIX_VECTORS];
+
+ /* Lock for HRRQ access */
+ spinlock_t hrrq_lock[PMCRAID_NUM_MSIX_VECTORS];
+
+ /* Expected toggle bit at host */
+ u8 host_toggle_bit[PMCRAID_NUM_MSIX_VECTORS];
+
+ /* No of Reset IOA retries . IOA marked dead if threshold exceeds */
+ u8 ioa_reset_attempts;
+#define PMCRAID_RESET_ATTEMPTS 3
+
+ /* Wait Q for threads to wait for Reset IOA completion */
+ wait_queue_head_t reset_wait_q;
+ struct pmcraid_cmd *reset_cmd;
+
+ /* structures for supporting SIGIO based AEN. */
+ struct fasync_struct *aen_queue;
+ struct mutex aen_queue_lock; /* lock for aen subscribers list */
+ struct cdev cdev;
+
+ struct Scsi_Host *host; /* mid layer interface structure handle */
+ struct pci_dev *pdev; /* PCI device structure handle */
+
+ u8 current_log_level; /* default level for logging IOASC errors */
+
+ u8 num_hrrq; /* Number of interrupt vectors allocated */
+ dev_t dev; /* Major-Minor numbers for Char device */
+
+ /* Used as ISR handler argument */
+ struct pmcraid_isr_param hrrq_vector[PMCRAID_NUM_MSIX_VECTORS];
+
+ /* configuration table */
+ struct pmcraid_config_table *cfg_table;
+ dma_addr_t cfg_table_bus_addr;
+
+ /* structures related to command blocks */
+ struct kmem_cache *cmd_cachep; /* cache for cmd blocks */
+ struct pci_pool *control_pool; /* pool for control blocks */
+ char cmd_pool_name[64]; /* name of cmd cache */
+ char ctl_pool_name[64]; /* name of control cache */
+
+ struct pmcraid_cmd *cmd_list[PMCRAID_MAX_CMD];
+
+ struct list_head free_cmd_pool;
+ struct list_head pending_cmd_pool;
+ spinlock_t free_pool_lock; /* free pool lock */
+ spinlock_t pending_pool_lock; /* pending pool lock */
+
+ /* No of IO commands pending with FW */
+ atomic_t outstanding_cmds;
+
+ /* should add/delete resources to mid-layer now ?*/
+ atomic_t expose_resources;
+
+ /* Tasklet to handle deferred processing */
+ struct tasklet_struct isr_tasklet[PMCRAID_NUM_MSIX_VECTORS];
+
+ /* Work-queue (Shared) for deferred reset processing */
+ struct work_struct worker_q;
+
+
+ u32 ioa_state:4; /* For IOA Reset sequence FSM */
+#define IOA_STATE_OPERATIONAL 0x0
+#define IOA_STATE_UNKNOWN 0x1
+#define IOA_STATE_DEAD 0x2
+#define IOA_STATE_IN_SOFT_RESET 0x3
+#define IOA_STATE_IN_HARD_RESET 0x4
+#define IOA_STATE_IN_RESET_ALERT 0x5
+#define IOA_STATE_IN_BRINGDOWN 0x6
+#define IOA_STATE_IN_BRINGUP 0x7
+
+ u32 ioa_reset_in_progress:1; /* true if IOA reset is in progress */
+ u32 ioa_hard_reset:1; /* TRUE if Hard Reset is needed */
+ u32 ioa_unit_check:1; /* Indicates Unit Check condition */
+ u32 ioa_bringdown:1; /* whether IOA needs to be brought down */
+ u32 force_ioa_reset:1; /* force adapter reset ? */
+ u32 reinit_cfg_table:1; /* reinit config table due to lost CCN */
+ u32 ioa_shutdown_type:2;/* shutdown type used during reset */
+#define SHUTDOWN_NONE 0x0
+#define SHUTDOWN_NORMAL 0x1
+#define SHUTDOWN_ABBREV 0x2
+
+};
+
+/* LLD maintained resource entry structure */
+struct pmcraid_resource_entry {
+ struct list_head queue; /* link to "to be exposed" resources */
+ struct pmcraid_config_table_entry cfg_entry;
+ struct scsi_device *scsi_dev; /* Link scsi_device structure */
+ atomic_t read_failures; /* count of failed READ commands */
+ atomic_t write_failures; /* count of failed WRITE commands */
+
+ /* To indicate add/delete/modify during CCN */
+ u8 change_detected;
+#define RES_CHANGE_ADD 0x1 /* add this to mid-layer */
+#define RES_CHANGE_DEL 0x2 /* remove this from mid-layer */
+
+ u8 reset_progress; /* Device is resetting */
+
+ /*
+ * When IOA asks for sync (i.e. IOASC = Not Ready, Sync Required), this
+ * flag will be set, mid layer will be asked to retry. In the next
+ * attempt, this flag will be checked in queuecommand() to set
+ * SYNC_COMPLETE flag in IOARCB (flag_0).
+ */
+ u8 sync_reqd;
+
+ /* target indicates the mapped target_id assigned to this resource if
+ * this is VSET resource. For non-VSET resources this will be un-used
+ * or zero
+ */
+ u8 target;
+};
+
+/* Data structures used in IOASC error code logging */
+struct pmcraid_ioasc_error {
+ u32 ioasc_code; /* IOASC code */
+ u8 log_level; /* default log level assignment. */
+ char *error_string;
+};
+
+/* Initial log_level assignments for various IOASCs */
+#define IOASC_LOG_LEVEL_NONE 0x0 /* no logging */
+#define IOASC_LOG_LEVEL_MUST 0x1 /* must log: all high-severity errors */
+#define IOASC_LOG_LEVEL_HARD 0x2 /* optional – low severity errors */
+
+/* Error information maintained by LLD. LLD initializes the pmcraid_error_table
+ * statically.
+ */
+static struct pmcraid_ioasc_error pmcraid_ioasc_error_table[] = {
+ {0x01180600, IOASC_LOG_LEVEL_MUST,
+ "Recovered Error, soft media error, sector reassignment suggested"},
+ {0x015D0000, IOASC_LOG_LEVEL_MUST,
+ "Recovered Error, failure prediction thresold exceeded"},
+ {0x015D9200, IOASC_LOG_LEVEL_MUST,
+ "Recovered Error, soft Cache Card Battery error thresold"},
+ {0x015D9200, IOASC_LOG_LEVEL_MUST,
+ "Recovered Error, soft Cache Card Battery error thresold"},
+ {0x02048000, IOASC_LOG_LEVEL_MUST,
+ "Not Ready, IOA Reset Required"},
+ {0x02408500, IOASC_LOG_LEVEL_MUST,
+ "Not Ready, IOA microcode download required"},
+ {0x03110B00, IOASC_LOG_LEVEL_MUST,
+ "Medium Error, data unreadable, reassignment suggested"},
+ {0x03110C00, IOASC_LOG_LEVEL_MUST,
+ "Medium Error, data unreadable do not reassign"},
+ {0x03310000, IOASC_LOG_LEVEL_MUST,
+ "Medium Error, media corrupted"},
+ {0x04050000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, IOA can't communicate with device"},
+ {0x04080000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, device bus error"},
+ {0x04080000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, device bus is not functioning"},
+ {0x04118000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, IOA reserved area data check"},
+ {0x04118100, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, IOA reserved area invalid data pattern"},
+ {0x04118200, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, IOA reserved area LRC error"},
+ {0x04320000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, reassignment space exhausted"},
+ {0x04330000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, data transfer underlength error"},
+ {0x04330000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, data transfer overlength error"},
+ {0x04418000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, PCI bus error"},
+ {0x04440000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, device error"},
+ {0x04448300, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, undefined device response"},
+ {0x04448400, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, IOA microcode error"},
+ {0x04448600, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, IOA reset required"},
+ {0x04449200, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, hard Cache Fearuee Card Battery error"},
+ {0x0444A000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, failed device altered"},
+ {0x0444A200, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, data check after reassignment"},
+ {0x0444A300, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, LRC error after reassignment"},
+ {0x044A0000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, device bus error (msg/cmd phase)"},
+ {0x04670400, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, new device can't be used"},
+ {0x04678000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, invalid multiadapter configuration"},
+ {0x04678100, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, incorrect connection between enclosures"},
+ {0x04678200, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, connections exceed IOA design limits"},
+ {0x04678300, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, incorrect multipath connection"},
+ {0x04679000, IOASC_LOG_LEVEL_MUST,
+ "Hardware Error, command to LUN failed"},
+ {0x064C8000, IOASC_LOG_LEVEL_HARD,
+ "Unit Attention, cache exists for missing/failed device"},
+ {0x06670100, IOASC_LOG_LEVEL_HARD,
+ "Unit Attention, incompatible exposed mode device"},
+ {0x06670600, IOASC_LOG_LEVEL_HARD,
+ "Unit Attention, attachment of logical unit failed"},
+ {0x06678000, IOASC_LOG_LEVEL_MUST,
+ "Unit Attention, cables exceed connective design limit"},
+ {0x06678300, IOASC_LOG_LEVEL_MUST,
+ "Unit Attention, incomplete multipath connection between" \
+ "IOA and enclosure"},
+ {0x06678400, IOASC_LOG_LEVEL_MUST,
+ "Unit Attention, incomplete multipath connection between" \
+ "device and enclosure"},
+ {0x06678500, IOASC_LOG_LEVEL_MUST,
+ "Unit Attention, incomplete multipath connection between" \
+ "IOA and remote IOA"},
+ {0x06678600, IOASC_LOG_LEVEL_HARD,
+ "Unit Attention, missing remote IOA"},
+ {0x06679100, IOASC_LOG_LEVEL_HARD,
+ "Unit Attention, enclosure doesn't support required multipath" \
+ "function"},
+ {0x06698200, IOASC_LOG_LEVEL_HARD,
+ "Unit Attention, corrupt array parity detected on device"},
+ {0x066B0200, IOASC_LOG_LEVEL_MUST,
+ "Unit Attention, array exposed"},
+ {0x066B8200, IOASC_LOG_LEVEL_HARD,
+ "Unit Attention, exposed array is still protected"},
+ {0x066B9200, IOASC_LOG_LEVEL_MUST,
+ "Unit Attention, Multipath redundancy level got worse"},
+ {0x07270000, IOASC_LOG_LEVEL_HARD,
+ "Data Protect, device is read/write protected by IOA"},
+ {0x07278000, IOASC_LOG_LEVEL_HARD,
+ "Data Protect, IOA doesn't support device attribute"},
+ {0x07278100, IOASC_LOG_LEVEL_HARD,
+ "Data Protect, NVRAM mirroring prohibited"},
+ {0x07278400, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, array is short 2 or more devices"},
+ {0x07278600, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, exposed array is short a required device"},
+ {0x07278700, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, array members not at required addresses"},
+ {0x07278800, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, exposed mode device resource address conflict"},
+ {0x07278900, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, incorrect resource address of exposed mode device"},
+ {0x07278A00, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, Array is missing a device and parity is out of sync"},
+ {0x07278B00, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, maximum number of arrays already exist"},
+ {0x07278C00, IOASC_LOG_LEVEL_HARD,
+ "Data Protect, cannot locate cache data for device"},
+ {0x07278D00, IOASC_LOG_LEVEL_HARD,
+ "Data Protect, cache data exits for a changed device"},
+ {0x07279100, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, detection of a device requiring format"},
+ {0x07279200, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, IOA exceeds maximum number of devices"},
+ {0x07279600, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, missing array, volume set is not functional"},
+ {0x07279700, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, single device for a volume set"},
+ {0x07279800, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, missing multiple devices for a volume set"},
+ {0x07279900, IOASC_LOG_LEVEL_HARD,
+ "Data Protect, maximum number of volument sets already exists"},
+ {0x07279A00, IOASC_LOG_LEVEL_MUST,
+ "Data Protect, other volume set problem"},
+};
+
+/* macros to help in debugging */
+#define pmcraid_err(...) \
+ printk(KERN_ERR "MaxRAID: "__VA_ARGS__)
+
+#define pmcraid_info(...) \
+ if (pmcraid_debug_log) \
+ printk(KERN_INFO "MaxRAID: "__VA_ARGS__)
+
+/* check if given command is a SCSI READ or SCSI WRITE command */
+#define SCSI_READ_CMD 0x1 /* any of SCSI READ commands */
+#define SCSI_WRITE_CMD 0x2 /* any of SCSI WRITE commands */
+#define SCSI_CMD_TYPE(opcode) \
+({ u8 op = opcode; u8 __type = 0;\
+ if (op == READ_6 || op == READ_10 || op == READ_12 || op == READ_16)\
+ __type = SCSI_READ_CMD;\
+ else if (op == WRITE_6 || op == WRITE_10 || op == WRITE_12 || \
+ op == WRITE_16)\
+ __type = SCSI_WRITE_CMD;\
+ __type;\
+})
+
+#define IS_SCSI_READ_WRITE(opcode) \
+({ u8 __type = SCSI_CMD_TYPE(opcode); \
+ (__type == SCSI_READ_CMD || __type == SCSI_WRITE_CMD) ? 1 : 0;\
+})
+
+
+/*
+ * pmcraid_ioctl_header - definition of header structure that preceeds all the
+ * buffers given as ioctl arguements.
+ *
+ * .signature : always ASCII string, "PMCRAID"
+ * .reserved : not used
+ * .buffer_length : length of the buffer following the header
+ */
+struct pmcraid_ioctl_header {
+ u8 signature[8];
+ u32 reserved;
+ u32 buffer_length;
+};
+
+#define PMCRAID_IOCTL_SIGNATURE "PMCRAID"
+
+
+/*
+ * pmcraid_event_details - defines AEN details that apps can retrieve from LLD
+ *
+ * .rcb_ccn - complete RCB of CCN
+ * .rcb_ldn - complete RCB of CCN
+ */
+struct pmcraid_event_details {
+ struct pmcraid_hcam_ccn rcb_ccn;
+ struct pmcraid_hcam_ldn rcb_ldn;
+};
+
+/*
+ * pmcraid_driver_ioctl_buffer - structure passed as argument to most of the
+ * PMC driver handled ioctls.
+ */
+struct pmcraid_driver_ioctl_buffer {
+ struct pmcraid_ioctl_header ioctl_header;
+ struct pmcraid_event_details event_details;
+};
+
+/*
+ * pmcraid_passthrough_ioctl_buffer - structure given as argument to
+ * passthrough(or firmware handled) IOCTL commands. Note that ioarcb requires
+ * 32-byte alignment so, it is necessary to pack this structure to avoid any
+ * holes between ioctl_header and passthrough buffer
+ *
+ * .ioactl_header : ioctl header
+ * .ioarcb : filled-up ioarcb buffer, driver always reads this buffer
+ * .ioasa : buffer for ioasa, driver fills this with IOASA from firmware
+ * .request_buffer: The I/O buffer (flat), driver reads/writes to this based on
+ * the transfer directions passed in ioarcb.flags0. Contents
+ * of this buffer are valid only when ioarcb.data_transfer_len
+ * is not zero.
+ */
+struct pmcraid_passthrough_ioctl_buffer {
+ struct pmcraid_ioctl_header ioctl_header;
+ struct pmcraid_ioarcb ioarcb;
+ struct pmcraid_ioasa ioasa;
+ u8 request_buffer[1];
+} __attribute__ ((packed));
+
+/*
+ * keys to differentiate between driver handled IOCTLs and passthrough
+ * IOCTLs passed to IOA. driver determines the ioctl type using macro
+ * _IOC_TYPE
+ */
+#define PMCRAID_DRIVER_IOCTL 'D'
+#define PMCRAID_PASSTHROUGH_IOCTL 'F'
+
+#define DRV_IOCTL(n, size) \
+ _IOC(_IOC_READ|_IOC_WRITE, PMCRAID_DRIVER_IOCTL, (n), (size))
+
+#define FMW_IOCTL(n, size) \
+ _IOC(_IOC_READ|_IOC_WRITE, PMCRAID_PASSTHROUGH_IOCTL, (n), (size))
+
+/*
+ * _ARGSIZE: macro that gives size of the argument type passed to an IOCTL cmd.
+ * This is to facilitate applications avoiding un-necessary memory allocations.
+ * For example, most of driver handled ioctls do not require ioarcb, ioasa.
+ */
+#define _ARGSIZE(arg) (sizeof(struct pmcraid_ioctl_header) + sizeof(arg))
+
+/* Driver handled IOCTL command definitions */
+
+#define PMCRAID_IOCTL_RESET_ADAPTER \
+ DRV_IOCTL(5, sizeof(struct pmcraid_ioctl_header))
+
+/* passthrough/firmware handled commands */
+#define PMCRAID_IOCTL_PASSTHROUGH_COMMAND \
+ FMW_IOCTL(1, sizeof(struct pmcraid_passthrough_ioctl_buffer))
+
+#define PMCRAID_IOCTL_DOWNLOAD_MICROCODE \
+ FMW_IOCTL(2, sizeof(struct pmcraid_passthrough_ioctl_buffer))
+
+
+#endif /* _PMCRAID_H */
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 0f879620150..fbcb82a2f7f 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -1670,7 +1670,7 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
qla24xx_vport_disable(fc_vport, disable);
- if (ql2xmultique_tag) {
+ if (ha->flags.cpu_affinity_enabled) {
req = ha->req_q_map[1];
goto vport_queue;
} else if (ql2xmaxqueues == 1 || !ha->npiv_info)
@@ -1736,6 +1736,11 @@ qla24xx_vport_delete(struct fc_vport *fc_vport)
qla24xx_deallocate_vp_id(vha);
+ mutex_lock(&ha->vport_lock);
+ ha->cur_vport_count--;
+ clear_bit(vha->vp_idx, ha->vp_idx_map);
+ mutex_unlock(&ha->vport_lock);
+
if (vha->timer_active) {
qla2x00_vp_stop_timer(vha);
DEBUG15(printk ("scsi(%ld): timer for the vport[%d] = %p "
@@ -1743,7 +1748,7 @@ qla24xx_vport_delete(struct fc_vport *fc_vport)
vha->host_no, vha->vp_idx, vha));
}
- if (vha->req->id && !ql2xmultique_tag) {
+ if (vha->req->id && !ha->flags.cpu_affinity_enabled) {
if (qla25xx_delete_req_que(vha, vha->req) != QLA_SUCCESS)
qla_printk(KERN_WARNING, ha,
"Queue delete failed.\n");
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index 00aa48d975a..21506186179 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -189,6 +189,7 @@ struct req_que;
*/
typedef struct srb {
struct fc_port *fcport;
+ uint32_t handle;
struct scsi_cmnd *cmd; /* Linux SCSI command pkt */
@@ -196,6 +197,8 @@ typedef struct srb {
uint32_t request_sense_length;
uint8_t *request_sense_ptr;
+
+ void *ctx;
} srb_t;
/*
@@ -204,6 +207,28 @@ typedef struct srb {
#define SRB_DMA_VALID BIT_0 /* Command sent to ISP */
/*
+ * SRB extensions.
+ */
+struct srb_ctx {
+#define SRB_LOGIN_CMD 1
+#define SRB_LOGOUT_CMD 2
+ uint16_t type;
+ struct timer_list timer;
+
+ void (*free)(srb_t *sp);
+ void (*timeout)(srb_t *sp);
+};
+
+struct srb_logio {
+ struct srb_ctx ctx;
+
+#define SRB_LOGIN_RETRIED BIT_0
+#define SRB_LOGIN_COND_PLOGI BIT_1
+#define SRB_LOGIN_SKIP_PRLI BIT_2
+ uint16_t flags;
+};
+
+/*
* ISP I/O Register Set structure definitions.
*/
struct device_reg_2xxx {
@@ -1482,7 +1507,7 @@ typedef union {
uint8_t domain;
uint8_t area;
uint8_t al_pa;
-#elif __LITTLE_ENDIAN
+#elif defined(__LITTLE_ENDIAN)
uint8_t al_pa;
uint8_t area;
uint8_t domain;
@@ -1565,6 +1590,7 @@ typedef struct fc_port {
#define FCF_FABRIC_DEVICE BIT_0
#define FCF_LOGIN_NEEDED BIT_1
#define FCF_TAPE_PRESENT BIT_2
+#define FCF_FCP2_DEVICE BIT_3
/* No loop ID flag. */
#define FC_NO_LOOP_ID 0x1000
@@ -2093,6 +2119,10 @@ struct qla_msix_entry {
enum qla_work_type {
QLA_EVT_AEN,
QLA_EVT_IDC_ACK,
+ QLA_EVT_ASYNC_LOGIN,
+ QLA_EVT_ASYNC_LOGIN_DONE,
+ QLA_EVT_ASYNC_LOGOUT,
+ QLA_EVT_ASYNC_LOGOUT_DONE,
};
@@ -2111,6 +2141,11 @@ struct qla_work_evt {
#define QLA_IDC_ACK_REGS 7
uint16_t mb[QLA_IDC_ACK_REGS];
} idc_ack;
+ struct {
+ struct fc_port *fcport;
+#define QLA_LOGIO_LOGIN_RETRIED BIT_0
+ u16 data[2];
+ } logio;
} u;
};
@@ -2224,6 +2259,7 @@ struct qla_hw_data {
uint32_t chip_reset_done :1;
uint32_t port0 :1;
uint32_t running_gold_fw :1;
+ uint32_t cpu_affinity_enabled :1;
} flags;
/* This spinlock is used to protect "io transactions", you must
@@ -2350,6 +2386,7 @@ struct qla_hw_data {
(ha)->flags.msix_enabled)
#define IS_FAC_REQUIRED(ha) (IS_QLA81XX(ha))
#define IS_NOCACHE_VPD_TYPE(ha) (IS_QLA81XX(ha))
+#define IS_ALOGIO_CAPABLE(ha) (IS_QLA23XX(ha) || IS_FWI2_CAPABLE(ha))
#define IS_IIDMA_CAPABLE(ha) ((ha)->device_type & DT_IIDMA)
#define IS_FWI2_CAPABLE(ha) ((ha)->device_type & DT_FWI2)
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index dfde2dd865c..66a8da5d7d0 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -1126,7 +1126,7 @@ struct vp_config_entry_24xx {
uint16_t id;
uint16_t reserved_4;
uint16_t hopct;
- uint8_t reserved_5;
+ uint8_t reserved_5[2];
};
#define VP_RPT_ID_IOCB_TYPE 0x32 /* Report ID Acquisition entry. */
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 65b12d82867..f3d1d1afa95 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -52,6 +52,14 @@ extern void qla2x00_try_to_stop_firmware(scsi_qla_host_t *);
extern void qla84xx_put_chip(struct scsi_qla_host *);
+extern int qla2x00_async_login(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+extern int qla2x00_async_logout(struct scsi_qla_host *, fc_port_t *);
+extern int qla2x00_async_login_done(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+extern int qla2x00_async_logout_done(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+
/*
* Global Data in qla_os.c source file.
*/
@@ -76,6 +84,15 @@ extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum
fc_host_event_code, u32);
extern int qla2x00_post_idc_ack_work(struct scsi_qla_host *, uint16_t *);
+extern int qla2x00_post_async_login_work(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+extern int qla2x00_post_async_login_done_work(struct scsi_qla_host *,
+ fc_port_t *, uint16_t *);
+extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+extern int qla2x00_post_async_logout_done_work(struct scsi_qla_host *,
+ fc_port_t *, uint16_t *);
+
extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *);
extern void qla2x00_abort_fcport_cmds(fc_port_t *);
@@ -83,6 +100,8 @@ extern struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *,
struct qla_hw_data *);
extern void qla2x00_free_host(struct scsi_qla_host *);
extern void qla2x00_relogin(struct scsi_qla_host *);
+extern void qla2x00_do_work(struct scsi_qla_host *);
+
/*
* Global Functions in qla_mid.c source file.
*/
@@ -135,6 +154,7 @@ int qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *,
uint16_t, uint16_t, uint8_t);
int __qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *,
uint16_t, uint16_t, uint8_t);
+extern int qla2x00_start_sp(srb_t *);
/*
* Global Function Prototypes in qla_mbx.c source file.
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 917534b9f22..4647015eba6 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -1674,6 +1674,10 @@ int
qla2x00_fdmi_register(scsi_qla_host_t *vha)
{
int rval;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return QLA_FUNCTION_FAILED;
rval = qla2x00_mgmt_svr_login(vha);
if (rval)
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index f2ce8e3cc91..9e3eaac2559 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -40,6 +40,210 @@ static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *);
static int qla84xx_init_chip(scsi_qla_host_t *);
static int qla25xx_init_queues(struct qla_hw_data *);
+/* SRB Extensions ---------------------------------------------------------- */
+
+static void
+qla2x00_ctx_sp_timeout(unsigned long __data)
+{
+ srb_t *sp = (srb_t *)__data;
+ struct srb_ctx *ctx;
+ fc_port_t *fcport = sp->fcport;
+ struct qla_hw_data *ha = fcport->vha->hw;
+ struct req_que *req;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ req = ha->req_q_map[0];
+ req->outstanding_cmds[sp->handle] = NULL;
+ ctx = sp->ctx;
+ ctx->timeout(sp);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ ctx->free(sp);
+}
+
+static void
+qla2x00_ctx_sp_free(srb_t *sp)
+{
+ struct srb_ctx *ctx = sp->ctx;
+
+ kfree(ctx);
+ mempool_free(sp, sp->fcport->vha->hw->srb_mempool);
+}
+
+inline srb_t *
+qla2x00_get_ctx_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size,
+ unsigned long tmo)
+{
+ srb_t *sp;
+ struct qla_hw_data *ha = vha->hw;
+ struct srb_ctx *ctx;
+
+ sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL);
+ if (!sp)
+ goto done;
+ ctx = kzalloc(size, GFP_KERNEL);
+ if (!ctx) {
+ mempool_free(sp, ha->srb_mempool);
+ goto done;
+ }
+
+ memset(sp, 0, sizeof(*sp));
+ sp->fcport = fcport;
+ sp->ctx = ctx;
+ ctx->free = qla2x00_ctx_sp_free;
+
+ init_timer(&ctx->timer);
+ if (!tmo)
+ goto done;
+ ctx->timer.expires = jiffies + tmo * HZ;
+ ctx->timer.data = (unsigned long)sp;
+ ctx->timer.function = qla2x00_ctx_sp_timeout;
+ add_timer(&ctx->timer);
+done:
+ return sp;
+}
+
+/* Asynchronous Login/Logout Routines -------------------------------------- */
+
+#define ELS_TMO_2_RATOV(ha) ((ha)->r_a_tov / 10 * 2)
+
+static void
+qla2x00_async_logio_timeout(srb_t *sp)
+{
+ fc_port_t *fcport = sp->fcport;
+ struct srb_logio *lio = sp->ctx;
+
+ DEBUG2(printk(KERN_WARNING
+ "scsi(%ld:%x): Async-%s timeout.\n",
+ fcport->vha->host_no, sp->handle,
+ lio->ctx.type == SRB_LOGIN_CMD ? "login": "logout"));
+
+ if (lio->ctx.type == SRB_LOGIN_CMD)
+ qla2x00_post_async_logout_work(fcport->vha, fcport, NULL);
+}
+
+int
+qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
+ uint16_t *data)
+{
+ struct qla_hw_data *ha = vha->hw;
+ srb_t *sp;
+ struct srb_logio *lio;
+ int rval;
+
+ rval = QLA_FUNCTION_FAILED;
+ sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_logio),
+ ELS_TMO_2_RATOV(ha) + 2);
+ if (!sp)
+ goto done;
+
+ lio = sp->ctx;
+ lio->ctx.type = SRB_LOGIN_CMD;
+ lio->ctx.timeout = qla2x00_async_logio_timeout;
+ lio->flags |= SRB_LOGIN_COND_PLOGI;
+ if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
+ lio->flags |= SRB_LOGIN_RETRIED;
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS)
+ goto done_free_sp;
+
+ DEBUG2(printk(KERN_DEBUG
+ "scsi(%ld:%x): Async-login - loop-id=%x portid=%02x%02x%02x "
+ "retries=%d.\n", fcport->vha->host_no, sp->handle, fcport->loop_id,
+ fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa,
+ fcport->login_retry));
+ return rval;
+
+done_free_sp:
+ del_timer_sync(&lio->ctx.timer);
+ lio->ctx.free(sp);
+done:
+ return rval;
+}
+
+int
+qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport)
+{
+ struct qla_hw_data *ha = vha->hw;
+ srb_t *sp;
+ struct srb_logio *lio;
+ int rval;
+
+ rval = QLA_FUNCTION_FAILED;
+ sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_logio),
+ ELS_TMO_2_RATOV(ha) + 2);
+ if (!sp)
+ goto done;
+
+ lio = sp->ctx;
+ lio->ctx.type = SRB_LOGOUT_CMD;
+ lio->ctx.timeout = qla2x00_async_logio_timeout;
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS)
+ goto done_free_sp;
+
+ DEBUG2(printk(KERN_DEBUG
+ "scsi(%ld:%x): Async-logout - loop-id=%x portid=%02x%02x%02x.\n",
+ fcport->vha->host_no, sp->handle, fcport->loop_id,
+ fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa));
+ return rval;
+
+done_free_sp:
+ del_timer_sync(&lio->ctx.timer);
+ lio->ctx.free(sp);
+done:
+ return rval;
+}
+
+int
+qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport,
+ uint16_t *data)
+{
+ int rval;
+ uint8_t opts = 0;
+
+ switch (data[0]) {
+ case MBS_COMMAND_COMPLETE:
+ if (fcport->flags & FCF_TAPE_PRESENT)
+ opts |= BIT_1;
+ rval = qla2x00_get_port_database(vha, fcport, opts);
+ if (rval != QLA_SUCCESS)
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
+ else
+ qla2x00_update_fcport(vha, fcport);
+ break;
+ case MBS_COMMAND_ERROR:
+ if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
+ set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+ else
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
+ break;
+ case MBS_PORT_ID_USED:
+ fcport->loop_id = data[1];
+ qla2x00_post_async_login_work(vha, fcport, NULL);
+ break;
+ case MBS_LOOP_ID_USED:
+ fcport->loop_id++;
+ rval = qla2x00_find_new_loop_id(vha, fcport);
+ if (rval != QLA_SUCCESS) {
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
+ break;
+ }
+ qla2x00_post_async_login_work(vha, fcport, NULL);
+ break;
+ }
+ return QLA_SUCCESS;
+}
+
+int
+qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport,
+ uint16_t *data)
+{
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
+ return QLA_SUCCESS;
+}
+
/****************************************************************************/
/* QLogic ISP2x00 Hardware Support Functions. */
/****************************************************************************/
@@ -987,7 +1191,6 @@ qla2x00_setup_chip(scsi_qla_host_t *vha)
ha->phy_version);
if (rval != QLA_SUCCESS)
goto failed;
-
ha->flags.npiv_supported = 0;
if (IS_QLA2XXX_MIDTYPE(ha) &&
(ha->fw_attributes & BIT_2)) {
@@ -1591,7 +1794,8 @@ qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len,
char *st, *en;
uint16_t index;
struct qla_hw_data *ha = vha->hw;
- int use_tbl = !IS_QLA25XX(ha) && !IS_QLA81XX(ha);
+ int use_tbl = !IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
+ !IS_QLA81XX(ha);
if (memcmp(model, BINZERO, len) != 0) {
strncpy(ha->model_number, model, len);
@@ -1978,7 +2182,7 @@ qla2x00_rport_del(void *data)
struct fc_rport *rport;
spin_lock_irq(fcport->vha->host->host_lock);
- rport = fcport->drport;
+ rport = fcport->drport ? fcport->drport: fcport->rport;
fcport->drport = NULL;
spin_unlock_irq(fcport->vha->host->host_lock);
if (rport)
@@ -2345,8 +2549,7 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport)
struct fc_rport *rport;
struct qla_hw_data *ha = vha->hw;
- if (fcport->drport)
- qla2x00_rport_del(fcport);
+ qla2x00_rport_del(fcport);
rport_ids.node_name = wwn_to_u64(fcport->node_name);
rport_ids.port_name = wwn_to_u64(fcport->port_name);
@@ -3039,6 +3242,12 @@ qla2x00_fabric_dev_login(scsi_qla_host_t *vha, fc_port_t *fcport,
rval = QLA_SUCCESS;
retry = 0;
+ if (IS_ALOGIO_CAPABLE(ha)) {
+ rval = qla2x00_post_async_login_work(vha, fcport, NULL);
+ if (!rval)
+ return rval;
+ }
+
rval = qla2x00_fabric_login(vha, fcport, next_loopid);
if (rval == QLA_SUCCESS) {
/* Send an ADISC to tape devices.*/
@@ -3133,7 +3342,7 @@ qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport,
} else {
fcport->port_type = FCT_TARGET;
if (mb[1] & BIT_1) {
- fcport->flags |= FCF_TAPE_PRESENT;
+ fcport->flags |= FCF_FCP2_DEVICE;
}
}
@@ -3244,7 +3453,7 @@ qla2x00_loop_resync(scsi_qla_host_t *vha)
struct req_que *req;
struct rsp_que *rsp;
- if (ql2xmultique_tag)
+ if (vha->hw->flags.cpu_affinity_enabled)
req = vha->hw->req_q_map[0];
else
req = vha->req;
@@ -3286,15 +3495,17 @@ qla2x00_loop_resync(scsi_qla_host_t *vha)
}
void
-qla2x00_update_fcports(scsi_qla_host_t *vha)
+qla2x00_update_fcports(scsi_qla_host_t *base_vha)
{
fc_port_t *fcport;
+ struct scsi_qla_host *tvp, *vha;
/* Go with deferred removal of rport references. */
- list_for_each_entry(fcport, &vha->vp_fcports, list)
- if (fcport && fcport->drport &&
- atomic_read(&fcport->state) != FCS_UNCONFIGURED)
- qla2x00_rport_del(fcport);
+ list_for_each_entry_safe(vha, tvp, &base_vha->hw->vp_list, list)
+ list_for_each_entry(fcport, &vha->vp_fcports, list)
+ if (fcport && fcport->drport &&
+ atomic_read(&fcport->state) != FCS_UNCONFIGURED)
+ qla2x00_rport_del(fcport);
}
/*
@@ -3331,8 +3542,6 @@ qla2x00_abort_isp(scsi_qla_host_t *vha)
if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
atomic_set(&vha->loop_state, LOOP_DOWN);
qla2x00_mark_all_devices_lost(vha, 0);
- list_for_each_entry_safe(vp, tvp, &ha->vp_list, list)
- qla2x00_mark_all_devices_lost(vp, 0);
} else {
if (!atomic_read(&vha->loop_down_timer))
atomic_set(&vha->loop_down_timer,
@@ -4264,7 +4473,7 @@ qla24xx_configure_vhba(scsi_qla_host_t *vha)
return -EINVAL;
rval = qla2x00_fw_ready(base_vha);
- if (ql2xmultique_tag)
+ if (ha->flags.cpu_affinity_enabled)
req = ha->req_q_map[0];
else
req = vha->req;
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 13396beae2c..c5ccac0bef7 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -350,6 +350,7 @@ qla2x00_start_scsi(srb_t *sp)
/* Build command packet */
req->current_outstanding_cmd = handle;
req->outstanding_cmds[handle] = sp;
+ sp->handle = handle;
sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
req->cnt -= req_cnt;
@@ -778,6 +779,7 @@ qla24xx_start_scsi(srb_t *sp)
/* Build command packet. */
req->current_outstanding_cmd = handle;
req->outstanding_cmds[handle] = sp;
+ sp->handle = handle;
sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
req->cnt -= req_cnt;
@@ -852,9 +854,211 @@ static void qla25xx_set_que(srb_t *sp, struct rsp_que **rsp)
struct qla_hw_data *ha = sp->fcport->vha->hw;
int affinity = cmd->request->cpu;
- if (ql2xmultique_tag && affinity >= 0 &&
+ if (ha->flags.cpu_affinity_enabled && affinity >= 0 &&
affinity < ha->max_rsp_queues - 1)
*rsp = ha->rsp_q_map[affinity + 1];
else
*rsp = ha->rsp_q_map[0];
}
+
+/* Generic Control-SRB manipulation functions. */
+
+static void *
+qla2x00_alloc_iocbs(srb_t *sp)
+{
+ scsi_qla_host_t *vha = sp->fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id);
+ uint32_t index, handle;
+ request_t *pkt;
+ uint16_t cnt, req_cnt;
+
+ pkt = NULL;
+ req_cnt = 1;
+
+ /* Check for room in outstanding command list. */
+ handle = req->current_outstanding_cmd;
+ for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
+ handle++;
+ if (handle == MAX_OUTSTANDING_COMMANDS)
+ handle = 1;
+ if (!req->outstanding_cmds[handle])
+ break;
+ }
+ if (index == MAX_OUTSTANDING_COMMANDS)
+ goto queuing_error;
+
+ /* Check for room on request queue. */
+ if (req->cnt < req_cnt) {
+ if (ha->mqenable)
+ cnt = RD_REG_DWORD(&reg->isp25mq.req_q_out);
+ else if (IS_FWI2_CAPABLE(ha))
+ cnt = RD_REG_DWORD(&reg->isp24.req_q_out);
+ else
+ cnt = qla2x00_debounce_register(
+ ISP_REQ_Q_OUT(ha, &reg->isp));
+
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
+ else
+ req->cnt = req->length -
+ (req->ring_index - cnt);
+ }
+ if (req->cnt < req_cnt)
+ goto queuing_error;
+
+ /* Prep packet */
+ req->current_outstanding_cmd = handle;
+ req->outstanding_cmds[handle] = sp;
+ req->cnt -= req_cnt;
+
+ pkt = req->ring_ptr;
+ memset(pkt, 0, REQUEST_ENTRY_SIZE);
+ pkt->entry_count = req_cnt;
+ pkt->handle = handle;
+ sp->handle = handle;
+
+queuing_error:
+ return pkt;
+}
+
+static void
+qla2x00_start_iocbs(srb_t *sp)
+{
+ struct qla_hw_data *ha = sp->fcport->vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id);
+ struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp;
+
+ /* Adjust ring index. */
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
+ } else
+ req->ring_ptr++;
+
+ /* Set chip new ring index. */
+ if (ha->mqenable) {
+ WRT_REG_DWORD(&reg->isp25mq.req_q_in, req->ring_index);
+ RD_REG_DWORD(&ioreg->hccr);
+ } else if (IS_FWI2_CAPABLE(ha)) {
+ WRT_REG_DWORD(&reg->isp24.req_q_in, req->ring_index);
+ RD_REG_DWORD_RELAXED(&reg->isp24.req_q_in);
+ } else {
+ WRT_REG_WORD(ISP_REQ_Q_IN(ha, &reg->isp), req->ring_index);
+ RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, &reg->isp));
+ }
+}
+
+static void
+qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+{
+ struct srb_logio *lio = sp->ctx;
+
+ logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
+ logio->control_flags = cpu_to_le16(LCF_COMMAND_PLOGI);
+ if (lio->flags & SRB_LOGIN_COND_PLOGI)
+ logio->control_flags |= cpu_to_le16(LCF_COND_PLOGI);
+ if (lio->flags & SRB_LOGIN_SKIP_PRLI)
+ logio->control_flags |= cpu_to_le16(LCF_SKIP_PRLI);
+ logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ logio->port_id[0] = sp->fcport->d_id.b.al_pa;
+ logio->port_id[1] = sp->fcport->d_id.b.area;
+ logio->port_id[2] = sp->fcport->d_id.b.domain;
+ logio->vp_index = sp->fcport->vp_idx;
+}
+
+static void
+qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx)
+{
+ struct qla_hw_data *ha = sp->fcport->vha->hw;
+ struct srb_logio *lio = sp->ctx;
+ uint16_t opts;
+
+ mbx->entry_type = MBX_IOCB_TYPE;;
+ SET_TARGET_ID(ha, mbx->loop_id, sp->fcport->loop_id);
+ mbx->mb0 = cpu_to_le16(MBC_LOGIN_FABRIC_PORT);
+ opts = lio->flags & SRB_LOGIN_COND_PLOGI ? BIT_0: 0;
+ opts |= lio->flags & SRB_LOGIN_SKIP_PRLI ? BIT_1: 0;
+ if (HAS_EXTENDED_IDS(ha)) {
+ mbx->mb1 = cpu_to_le16(sp->fcport->loop_id);
+ mbx->mb10 = cpu_to_le16(opts);
+ } else {
+ mbx->mb1 = cpu_to_le16((sp->fcport->loop_id << 8) | opts);
+ }
+ mbx->mb2 = cpu_to_le16(sp->fcport->d_id.b.domain);
+ mbx->mb3 = cpu_to_le16(sp->fcport->d_id.b.area << 8 |
+ sp->fcport->d_id.b.al_pa);
+ mbx->mb9 = cpu_to_le16(sp->fcport->vp_idx);
+}
+
+static void
+qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+{
+ logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
+ logio->control_flags =
+ cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO);
+ logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ logio->port_id[0] = sp->fcport->d_id.b.al_pa;
+ logio->port_id[1] = sp->fcport->d_id.b.area;
+ logio->port_id[2] = sp->fcport->d_id.b.domain;
+ logio->vp_index = sp->fcport->vp_idx;
+}
+
+static void
+qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx)
+{
+ struct qla_hw_data *ha = sp->fcport->vha->hw;
+
+ mbx->entry_type = MBX_IOCB_TYPE;;
+ SET_TARGET_ID(ha, mbx->loop_id, sp->fcport->loop_id);
+ mbx->mb0 = cpu_to_le16(MBC_LOGOUT_FABRIC_PORT);
+ mbx->mb1 = HAS_EXTENDED_IDS(ha) ?
+ cpu_to_le16(sp->fcport->loop_id):
+ cpu_to_le16(sp->fcport->loop_id << 8);
+ mbx->mb2 = cpu_to_le16(sp->fcport->d_id.b.domain);
+ mbx->mb3 = cpu_to_le16(sp->fcport->d_id.b.area << 8 |
+ sp->fcport->d_id.b.al_pa);
+ mbx->mb9 = cpu_to_le16(sp->fcport->vp_idx);
+ /* Implicit: mbx->mbx10 = 0. */
+}
+
+int
+qla2x00_start_sp(srb_t *sp)
+{
+ int rval;
+ struct qla_hw_data *ha = sp->fcport->vha->hw;
+ void *pkt;
+ struct srb_ctx *ctx = sp->ctx;
+ unsigned long flags;
+
+ rval = QLA_FUNCTION_FAILED;
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ pkt = qla2x00_alloc_iocbs(sp);
+ if (!pkt)
+ goto done;
+
+ rval = QLA_SUCCESS;
+ switch (ctx->type) {
+ case SRB_LOGIN_CMD:
+ IS_FWI2_CAPABLE(ha) ?
+ qla24xx_login_iocb(sp, pkt):
+ qla2x00_login_iocb(sp, pkt);
+ break;
+ case SRB_LOGOUT_CMD:
+ IS_FWI2_CAPABLE(ha) ?
+ qla24xx_logout_iocb(sp, pkt):
+ qla2x00_logout_iocb(sp, pkt);
+ break;
+ default:
+ break;
+ }
+
+ wmb();
+ qla2x00_start_iocbs(sp);
+done:
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ return rval;
+}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 245e7afb4c4..b20a7169aac 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -598,9 +598,54 @@ skip_rio:
break;
case MBA_PORT_UPDATE: /* Port database update */
- /* Only handle SCNs for our Vport index. */
- if (vha->vp_idx && vha->vp_idx != (mb[3] & 0xff))
+ /*
+ * Handle only global and vn-port update events
+ *
+ * Relevant inputs:
+ * mb[1] = N_Port handle of changed port
+ * OR 0xffff for global event
+ * mb[2] = New login state
+ * 7 = Port logged out
+ * mb[3] = LSB is vp_idx, 0xff = all vps
+ *
+ * Skip processing if:
+ * Event is global, vp_idx is NOT all vps,
+ * vp_idx does not match
+ * Event is not global, vp_idx does not match
+ */
+ if ((mb[1] == 0xffff && (mb[3] & 0xff) != 0xff)
+ || (mb[1] != 0xffff)) {
+ if (vha->vp_idx != (mb[3] & 0xff))
+ break;
+ }
+
+ /* Global event -- port logout or port unavailable. */
+ if (mb[1] == 0xffff && mb[2] == 0x7) {
+ DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE.\n",
+ vha->host_no));
+ DEBUG(printk(KERN_INFO
+ "scsi(%ld): Port unavailable %04x %04x %04x.\n",
+ vha->host_no, mb[1], mb[2], mb[3]));
+
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer,
+ LOOP_DOWN_TIME);
+ vha->device_flags |= DFLG_NO_CABLE;
+ qla2x00_mark_all_devices_lost(vha, 1);
+ }
+
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport,
+ FC_VPORT_FAILED);
+ qla2x00_mark_all_devices_lost(vha, 1);
+ }
+
+ vha->flags.management_server_logged_in = 0;
+ ha->link_data_rate = PORT_SPEED_UNKNOWN;
break;
+ }
/*
* If PORT UPDATE is global (received LIP_OCCURRED/LIP_RESET
@@ -640,8 +685,9 @@ skip_rio:
if (vha->vp_idx && test_bit(VP_SCR_NEEDED, &vha->vp_flags))
break;
/* Only handle SCNs for our Vport index. */
- if (vha->vp_idx && vha->vp_idx != (mb[3] & 0xff))
+ if (ha->flags.npiv_supported && vha->vp_idx != (mb[3] & 0xff))
break;
+
DEBUG2(printk("scsi(%ld): Asynchronous RSCR UPDATE.\n",
vha->host_no));
DEBUG(printk(KERN_INFO
@@ -874,6 +920,249 @@ qla2x00_process_completed_request(struct scsi_qla_host *vha,
}
}
+static srb_t *
+qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func,
+ struct req_que *req, void *iocb)
+{
+ struct qla_hw_data *ha = vha->hw;
+ sts_entry_t *pkt = iocb;
+ srb_t *sp = NULL;
+ uint16_t index;
+
+ index = LSW(pkt->handle);
+ if (index >= MAX_OUTSTANDING_COMMANDS) {
+ qla_printk(KERN_WARNING, ha,
+ "%s: Invalid completion handle (%x).\n", func, index);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ goto done;
+ }
+ sp = req->outstanding_cmds[index];
+ if (!sp) {
+ qla_printk(KERN_WARNING, ha,
+ "%s: Invalid completion handle (%x) -- timed-out.\n", func,
+ index);
+ return sp;
+ }
+ if (sp->handle != index) {
+ qla_printk(KERN_WARNING, ha,
+ "%s: SRB handle (%x) mismatch %x.\n", func, sp->handle,
+ index);
+ return NULL;
+ }
+ req->outstanding_cmds[index] = NULL;
+done:
+ return sp;
+}
+
+static void
+qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
+ struct mbx_entry *mbx)
+{
+ const char func[] = "MBX-IOCB";
+ const char *type;
+ struct qla_hw_data *ha = vha->hw;
+ fc_port_t *fcport;
+ srb_t *sp;
+ struct srb_logio *lio;
+ uint16_t data[2];
+
+ sp = qla2x00_get_sp_from_handle(vha, func, req, mbx);
+ if (!sp)
+ return;
+
+ type = NULL;
+ lio = sp->ctx;
+ switch (lio->ctx.type) {
+ case SRB_LOGIN_CMD:
+ type = "login";
+ break;
+ case SRB_LOGOUT_CMD:
+ type = "logout";
+ break;
+ default:
+ qla_printk(KERN_WARNING, ha,
+ "%s: Unrecognized SRB: (%p) type=%d.\n", func, sp,
+ lio->ctx.type);
+ return;
+ }
+
+ del_timer(&lio->ctx.timer);
+ fcport = sp->fcport;
+
+ data[0] = data[1] = 0;
+ if (mbx->entry_status) {
+ DEBUG2(printk(KERN_WARNING
+ "scsi(%ld:%x): Async-%s error entry - entry-status=%x "
+ "status=%x state-flag=%x status-flags=%x.\n",
+ fcport->vha->host_no, sp->handle, type,
+ mbx->entry_status, le16_to_cpu(mbx->status),
+ le16_to_cpu(mbx->state_flags),
+ le16_to_cpu(mbx->status_flags)));
+ DEBUG2(qla2x00_dump_buffer((uint8_t *)mbx, sizeof(*mbx)));
+
+ data[0] = MBS_COMMAND_ERROR;
+ data[1] = lio->flags & SRB_LOGIN_RETRIED ?
+ QLA_LOGIO_LOGIN_RETRIED: 0;
+ goto done_post_logio_done_work;
+ }
+
+ if (!mbx->status && le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE) {
+ DEBUG2(printk(KERN_DEBUG
+ "scsi(%ld:%x): Async-%s complete - mbx1=%x.\n",
+ fcport->vha->host_no, sp->handle, type,
+ le16_to_cpu(mbx->mb1)));
+
+ data[0] = MBS_COMMAND_COMPLETE;
+ if (lio->ctx.type == SRB_LOGIN_CMD && le16_to_cpu(mbx->mb1) & BIT_1)
+ fcport->flags |= FCF_FCP2_DEVICE;
+
+ goto done_post_logio_done_work;
+ }
+
+ data[0] = le16_to_cpu(mbx->mb0);
+ switch (data[0]) {
+ case MBS_PORT_ID_USED:
+ data[1] = le16_to_cpu(mbx->mb1);
+ break;
+ case MBS_LOOP_ID_USED:
+ break;
+ default:
+ data[0] = MBS_COMMAND_ERROR;
+ data[1] = lio->flags & SRB_LOGIN_RETRIED ?
+ QLA_LOGIO_LOGIN_RETRIED: 0;
+ break;
+ }
+
+ DEBUG2(printk(KERN_WARNING
+ "scsi(%ld:%x): Async-%s failed - status=%x mb0=%x mb1=%x mb2=%x "
+ "mb6=%x mb7=%x.\n",
+ fcport->vha->host_no, sp->handle, type, le16_to_cpu(mbx->status),
+ le16_to_cpu(mbx->mb0), le16_to_cpu(mbx->mb1),
+ le16_to_cpu(mbx->mb2), le16_to_cpu(mbx->mb6),
+ le16_to_cpu(mbx->mb7)));
+
+done_post_logio_done_work:
+ lio->ctx.type == SRB_LOGIN_CMD ?
+ qla2x00_post_async_login_done_work(fcport->vha, fcport, data):
+ qla2x00_post_async_logout_done_work(fcport->vha, fcport, data);
+
+ lio->ctx.free(sp);
+}
+
+static void
+qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
+ struct logio_entry_24xx *logio)
+{
+ const char func[] = "LOGIO-IOCB";
+ const char *type;
+ struct qla_hw_data *ha = vha->hw;
+ fc_port_t *fcport;
+ srb_t *sp;
+ struct srb_logio *lio;
+ uint16_t data[2];
+ uint32_t iop[2];
+
+ sp = qla2x00_get_sp_from_handle(vha, func, req, logio);
+ if (!sp)
+ return;
+
+ type = NULL;
+ lio = sp->ctx;
+ switch (lio->ctx.type) {
+ case SRB_LOGIN_CMD:
+ type = "login";
+ break;
+ case SRB_LOGOUT_CMD:
+ type = "logout";
+ break;
+ default:
+ qla_printk(KERN_WARNING, ha,
+ "%s: Unrecognized SRB: (%p) type=%d.\n", func, sp,
+ lio->ctx.type);
+ return;
+ }
+
+ del_timer(&lio->ctx.timer);
+ fcport = sp->fcport;
+
+ data[0] = data[1] = 0;
+ if (logio->entry_status) {
+ DEBUG2(printk(KERN_WARNING
+ "scsi(%ld:%x): Async-%s error entry - entry-status=%x.\n",
+ fcport->vha->host_no, sp->handle, type,
+ logio->entry_status));
+ DEBUG2(qla2x00_dump_buffer((uint8_t *)logio, sizeof(*logio)));
+
+ data[0] = MBS_COMMAND_ERROR;
+ data[1] = lio->flags & SRB_LOGIN_RETRIED ?
+ QLA_LOGIO_LOGIN_RETRIED: 0;
+ goto done_post_logio_done_work;
+ }
+
+ if (le16_to_cpu(logio->comp_status) == CS_COMPLETE) {
+ DEBUG2(printk(KERN_DEBUG
+ "scsi(%ld:%x): Async-%s complete - iop0=%x.\n",
+ fcport->vha->host_no, sp->handle, type,
+ le32_to_cpu(logio->io_parameter[0])));
+
+ data[0] = MBS_COMMAND_COMPLETE;
+ if (lio->ctx.type == SRB_LOGOUT_CMD)
+ goto done_post_logio_done_work;
+
+ iop[0] = le32_to_cpu(logio->io_parameter[0]);
+ if (iop[0] & BIT_4) {
+ fcport->port_type = FCT_TARGET;
+ if (iop[0] & BIT_8)
+ fcport->flags |= FCF_FCP2_DEVICE;
+ }
+ if (iop[0] & BIT_5)
+ fcport->port_type = FCT_INITIATOR;
+ if (logio->io_parameter[7] || logio->io_parameter[8])
+ fcport->supported_classes |= FC_COS_CLASS2;
+ if (logio->io_parameter[9] || logio->io_parameter[10])
+ fcport->supported_classes |= FC_COS_CLASS3;
+
+ goto done_post_logio_done_work;
+ }
+
+ iop[0] = le32_to_cpu(logio->io_parameter[0]);
+ iop[1] = le32_to_cpu(logio->io_parameter[1]);
+ switch (iop[0]) {
+ case LSC_SCODE_PORTID_USED:
+ data[0] = MBS_PORT_ID_USED;
+ data[1] = LSW(iop[1]);
+ break;
+ case LSC_SCODE_NPORT_USED:
+ data[0] = MBS_LOOP_ID_USED;
+ break;
+ case LSC_SCODE_CMD_FAILED:
+ if ((iop[1] & 0xff) == 0x05) {
+ data[0] = MBS_NOT_LOGGED_IN;
+ break;
+ }
+ /* Fall through. */
+ default:
+ data[0] = MBS_COMMAND_ERROR;
+ data[1] = lio->flags & SRB_LOGIN_RETRIED ?
+ QLA_LOGIO_LOGIN_RETRIED: 0;
+ break;
+ }
+
+ DEBUG2(printk(KERN_WARNING
+ "scsi(%ld:%x): Async-%s failed - comp=%x iop0=%x iop1=%x.\n",
+ fcport->vha->host_no, sp->handle, type,
+ le16_to_cpu(logio->comp_status),
+ le32_to_cpu(logio->io_parameter[0]),
+ le32_to_cpu(logio->io_parameter[1])));
+
+done_post_logio_done_work:
+ lio->ctx.type == SRB_LOGIN_CMD ?
+ qla2x00_post_async_login_done_work(fcport->vha, fcport, data):
+ qla2x00_post_async_logout_done_work(fcport->vha, fcport, data);
+
+ lio->ctx.free(sp);
+}
+
/**
* qla2x00_process_response_queue() - Process response queue entries.
* @ha: SCSI driver HA context
@@ -935,6 +1224,9 @@ qla2x00_process_response_queue(struct rsp_que *rsp)
case STATUS_CONT_TYPE:
qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
break;
+ case MBX_IOCB_TYPE:
+ qla2x00_mbx_iocb_entry(vha, rsp->req,
+ (struct mbx_entry *)pkt);
default:
/* Type Not Supported. */
DEBUG4(printk(KERN_WARNING
@@ -1223,6 +1515,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
cp->device->id, cp->device->lun, resid,
scsi_bufflen(cp)));
+ scsi_set_resid(cp, resid);
cp->result = DID_ERROR << 16;
break;
}
@@ -1544,6 +1837,10 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha,
qla24xx_report_id_acquisition(vha,
(struct vp_rpt_id_entry_24xx *)pkt);
break;
+ case LOGINOUT_PORT_IOCB_TYPE:
+ qla24xx_logio_entry(vha, rsp->req,
+ (struct logio_entry_24xx *)pkt);
+ break;
default:
/* Type Not Supported. */
DEBUG4(printk(KERN_WARNING
@@ -1723,8 +2020,10 @@ qla24xx_msix_rsp_q(int irq, void *dev_id)
vha = qla25xx_get_host(rsp);
qla24xx_process_response_queue(vha, rsp);
- WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
-
+ if (!ha->mqenable) {
+ WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
+ RD_REG_DWORD_RELAXED(&reg->hccr);
+ }
spin_unlock_irq(&ha->hardware_lock);
return IRQ_HANDLED;
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index fe69f305767..b6202fe118a 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -1507,7 +1507,7 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no));
- if (ql2xmultique_tag)
+ if (ha->flags.cpu_affinity_enabled)
req = ha->req_q_map[0];
else
req = vha->req;
@@ -2324,7 +2324,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
vha = fcport->vha;
ha = vha->hw;
req = vha->req;
- if (ql2xmultique_tag)
+ if (ha->flags.cpu_affinity_enabled)
rsp = ha->rsp_q_map[tag + 1];
else
rsp = req->rsp;
@@ -2746,7 +2746,8 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
if (rptid_entry->format == 0) {
DEBUG15(printk("%s:format 0 : scsi(%ld) number of VPs setup %d,"
" number of VPs acquired %d\n", __func__, vha->host_no,
- MSB(rptid_entry->vp_count), LSB(rptid_entry->vp_count)));
+ MSB(le16_to_cpu(rptid_entry->vp_count)),
+ LSB(le16_to_cpu(rptid_entry->vp_count))));
DEBUG15(printk("%s primary port id %02x%02x%02x\n", __func__,
rptid_entry->port_id[2], rptid_entry->port_id[1],
rptid_entry->port_id[0]));
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index cd78c501803..42b799abba5 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -42,7 +42,6 @@ qla24xx_allocate_vp_id(scsi_qla_host_t *vha)
set_bit(vp_id, ha->vp_idx_map);
ha->num_vhosts++;
- ha->cur_vport_count++;
vha->vp_idx = vp_id;
list_add_tail(&vha->list, &ha->vp_list);
mutex_unlock(&ha->vport_lock);
@@ -58,7 +57,6 @@ qla24xx_deallocate_vp_id(scsi_qla_host_t *vha)
mutex_lock(&ha->vport_lock);
vp_id = vha->vp_idx;
ha->num_vhosts--;
- ha->cur_vport_count--;
clear_bit(vp_id, ha->vp_idx_map);
list_del(&vha->list);
mutex_unlock(&ha->vport_lock);
@@ -235,7 +233,11 @@ qla2x00_vp_abort_isp(scsi_qla_host_t *vha)
atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
}
- /* To exclusively reset vport, we need to log it out first.*/
+ /*
+ * To exclusively reset vport, we need to log it out first. Note: this
+ * control_vp can fail if ISP reset is already issued, this is
+ * expected, as the vp would be already logged out due to ISP reset.
+ */
if (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL);
@@ -247,18 +249,11 @@ qla2x00_vp_abort_isp(scsi_qla_host_t *vha)
static int
qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
{
- struct qla_hw_data *ha = vha->hw;
- scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
+ qla2x00_do_work(vha);
if (test_and_clear_bit(VP_IDX_ACQUIRED, &vha->vp_flags)) {
/* VP acquired. complete port configuration */
- if (atomic_read(&base_vha->loop_state) == LOOP_READY) {
- qla24xx_configure_vp(vha);
- } else {
- set_bit(VP_IDX_ACQUIRED, &vha->vp_flags);
- set_bit(VP_DPC_NEEDED, &base_vha->dpc_flags);
- }
-
+ qla24xx_configure_vp(vha);
return 0;
}
@@ -309,6 +304,9 @@ qla2x00_do_dpc_all_vps(scsi_qla_host_t *vha)
clear_bit(VP_DPC_NEEDED, &vha->dpc_flags);
+ if (!(ha->current_topology & ISP_CFG_F))
+ return;
+
list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
if (vp->vp_idx)
ret = qla2x00_do_dpc_vp(vp);
@@ -413,6 +411,11 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
vha->flags.init_done = 1;
+ mutex_lock(&ha->vport_lock);
+ set_bit(vha->vp_idx, ha->vp_idx_map);
+ ha->cur_vport_count++;
+ mutex_unlock(&ha->vport_lock);
+
return vha;
create_vhost_failed:
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index f0396e79b6f..b79fca7d461 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -287,9 +287,12 @@ static int qla25xx_setup_mode(struct scsi_qla_host *vha)
int ques, req, ret;
struct qla_hw_data *ha = vha->hw;
+ if (!(ha->fw_attributes & BIT_6)) {
+ qla_printk(KERN_INFO, ha,
+ "Firmware is not multi-queue capable\n");
+ goto fail;
+ }
if (ql2xmultique_tag) {
- /* CPU affinity mode */
- ha->wq = create_workqueue("qla2xxx_wq");
/* create a request queue for IO */
options |= BIT_7;
req = qla25xx_create_req_que(ha, options, 0, 0, -1,
@@ -299,6 +302,7 @@ static int qla25xx_setup_mode(struct scsi_qla_host *vha)
"Can't create request queue\n");
goto fail;
}
+ ha->wq = create_workqueue("qla2xxx_wq");
vha->req = ha->req_q_map[req];
options |= BIT_1;
for (ques = 1; ques < ha->max_rsp_queues; ques++) {
@@ -309,6 +313,8 @@ static int qla25xx_setup_mode(struct scsi_qla_host *vha)
goto fail2;
}
}
+ ha->flags.cpu_affinity_enabled = 1;
+
DEBUG2(qla_printk(KERN_INFO, ha,
"CPU affinity mode enabled, no. of response"
" queues:%d, no. of request queues:%d\n",
@@ -317,8 +323,13 @@ static int qla25xx_setup_mode(struct scsi_qla_host *vha)
return 0;
fail2:
qla25xx_delete_queues(vha);
+ destroy_workqueue(ha->wq);
+ ha->wq = NULL;
fail:
ha->mqenable = 0;
+ kfree(ha->req_q_map);
+ kfree(ha->rsp_q_map);
+ ha->max_req_queues = ha->max_rsp_queues = 1;
return 1;
}
@@ -462,6 +473,7 @@ qla2x00_get_new_sp(scsi_qla_host_t *vha, fc_port_t *fcport,
sp->flags = 0;
CMD_SP(cmd) = (void *)sp;
cmd->scsi_done = done;
+ sp->ctx = NULL;
return sp;
}
@@ -556,11 +568,8 @@ qla2x00_eh_wait_on_command(struct scsi_cmnd *cmd)
unsigned long wait_iter = ABORT_WAIT_ITER;
int ret = QLA_SUCCESS;
- while (CMD_SP(cmd)) {
+ while (CMD_SP(cmd) && wait_iter--) {
msleep(ABORT_POLLING_PERIOD);
-
- if (--wait_iter)
- break;
}
if (CMD_SP(cmd))
ret = QLA_FUNCTION_FAILED;
@@ -698,6 +707,8 @@ qla2x00_abort_fcport_cmds(fc_port_t *fcport)
continue;
if (sp->fcport != fcport)
continue;
+ if (sp->ctx)
+ continue;
spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (ha->isp_ops->abort_command(sp)) {
@@ -783,7 +794,8 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
if (sp == NULL)
continue;
-
+ if (sp->ctx)
+ continue;
if (sp->cmd != cmd)
continue;
@@ -848,7 +860,8 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t,
sp = req->outstanding_cmds[cnt];
if (!sp)
continue;
-
+ if (sp->ctx)
+ continue;
if (vha->vp_idx != sp->fcport->vha->vp_idx)
continue;
match = 0;
@@ -1106,8 +1119,7 @@ qla2x00_loop_reset(scsi_qla_host_t *vha)
struct fc_port *fcport;
struct qla_hw_data *ha = vha->hw;
- if (ha->flags.enable_lip_full_login && !vha->vp_idx &&
- !IS_QLA81XX(ha)) {
+ if (ha->flags.enable_lip_full_login && !IS_QLA81XX(ha)) {
ret = qla2x00_full_login_lip(vha);
if (ret != QLA_SUCCESS) {
DEBUG2_3(printk("%s(%ld): failed: "
@@ -1120,7 +1132,7 @@ qla2x00_loop_reset(scsi_qla_host_t *vha)
qla2x00_wait_for_loop_ready(vha);
}
- if (ha->flags.enable_lip_reset && !vha->vp_idx) {
+ if (ha->flags.enable_lip_reset) {
ret = qla2x00_lip_reset(vha);
if (ret != QLA_SUCCESS) {
DEBUG2_3(printk("%s(%ld): failed: "
@@ -1154,6 +1166,7 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
int que, cnt;
unsigned long flags;
srb_t *sp;
+ struct srb_ctx *ctx;
struct qla_hw_data *ha = vha->hw;
struct req_que *req;
@@ -1166,8 +1179,14 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
sp = req->outstanding_cmds[cnt];
if (sp) {
req->outstanding_cmds[cnt] = NULL;
- sp->cmd->result = res;
- qla2x00_sp_compl(ha, sp);
+ if (!sp->ctx) {
+ sp->cmd->result = res;
+ qla2x00_sp_compl(ha, sp);
+ } else {
+ ctx = sp->ctx;
+ del_timer_sync(&ctx->timer);
+ ctx->free(sp);
+ }
}
}
}
@@ -1193,6 +1212,7 @@ qla2xxx_slave_configure(struct scsi_device *sdev)
scsi_qla_host_t *vha = shost_priv(sdev->host);
struct qla_hw_data *ha = vha->hw;
struct fc_rport *rport = starget_to_rport(sdev->sdev_target);
+ fc_port_t *fcport = *(fc_port_t **)rport->dd_data;
struct req_que *req = vha->req;
if (sdev->tagged_supported)
@@ -1201,6 +1221,8 @@ qla2xxx_slave_configure(struct scsi_device *sdev)
scsi_deactivate_tcq(sdev, req->max_q_depth);
rport->dev_loss_tmo = ha->port_down_retry_count;
+ if (sdev->type == TYPE_TAPE)
+ fcport->flags |= FCF_TAPE_PRESENT;
return 0;
}
@@ -1923,6 +1945,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (ret)
goto probe_init_failed;
/* Alloc arrays of request and response ring ptrs */
+que_init:
if (!qla2x00_alloc_queues(ha)) {
qla_printk(KERN_WARNING, ha,
"[ERROR] Failed to allocate memory for queue"
@@ -1959,11 +1982,14 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
goto probe_failed;
}
- if (ha->mqenable)
- if (qla25xx_setup_mode(base_vha))
+ if (ha->mqenable) {
+ if (qla25xx_setup_mode(base_vha)) {
qla_printk(KERN_WARNING, ha,
"Can't create queues, falling back to single"
" queue mode\n");
+ goto que_init;
+ }
+ }
if (ha->flags.running_gold_fw)
goto skip_dpc;
@@ -2155,17 +2181,19 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport,
int defer)
{
struct fc_rport *rport;
+ scsi_qla_host_t *base_vha;
if (!fcport->rport)
return;
rport = fcport->rport;
if (defer) {
+ base_vha = pci_get_drvdata(vha->hw->pdev);
spin_lock_irq(vha->host->host_lock);
fcport->drport = rport;
spin_unlock_irq(vha->host->host_lock);
- set_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags);
- qla2xxx_wake_dpc(vha);
+ set_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
+ qla2xxx_wake_dpc(base_vha);
} else
fc_remote_port_delete(rport);
}
@@ -2237,8 +2265,9 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
fc_port_t *fcport;
list_for_each_entry(fcport, &vha->vp_fcports, list) {
- if (vha->vp_idx != fcport->vp_idx)
+ if (vha->vp_idx != 0 && vha->vp_idx != fcport->vp_idx)
continue;
+
/*
* No point in marking the device as lost, if the device is
* already DEAD.
@@ -2246,10 +2275,12 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
continue;
if (atomic_read(&fcport->state) == FCS_ONLINE) {
- atomic_set(&fcport->state, FCS_DEVICE_LOST);
- qla2x00_schedule_rport_del(vha, fcport, defer);
- } else
- atomic_set(&fcport->state, FCS_DEVICE_LOST);
+ if (defer)
+ qla2x00_schedule_rport_del(vha, fcport, defer);
+ else if (vha->vp_idx == fcport->vp_idx)
+ qla2x00_schedule_rport_del(vha, fcport, defer);
+ }
+ atomic_set(&fcport->state, FCS_DEVICE_LOST);
}
}
@@ -2598,7 +2629,31 @@ qla2x00_post_idc_ack_work(struct scsi_qla_host *vha, uint16_t *mb)
return qla2x00_post_work(vha, e);
}
-static void
+#define qla2x00_post_async_work(name, type) \
+int qla2x00_post_async_##name##_work( \
+ struct scsi_qla_host *vha, \
+ fc_port_t *fcport, uint16_t *data) \
+{ \
+ struct qla_work_evt *e; \
+ \
+ e = qla2x00_alloc_work(vha, type); \
+ if (!e) \
+ return QLA_FUNCTION_FAILED; \
+ \
+ e->u.logio.fcport = fcport; \
+ if (data) { \
+ e->u.logio.data[0] = data[0]; \
+ e->u.logio.data[1] = data[1]; \
+ } \
+ return qla2x00_post_work(vha, e); \
+}
+
+qla2x00_post_async_work(login, QLA_EVT_ASYNC_LOGIN);
+qla2x00_post_async_work(login_done, QLA_EVT_ASYNC_LOGIN_DONE);
+qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT);
+qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE);
+
+void
qla2x00_do_work(struct scsi_qla_host *vha)
{
struct qla_work_evt *e, *tmp;
@@ -2620,6 +2675,21 @@ qla2x00_do_work(struct scsi_qla_host *vha)
case QLA_EVT_IDC_ACK:
qla81xx_idc_ack(vha, e->u.idc_ack.mb);
break;
+ case QLA_EVT_ASYNC_LOGIN:
+ qla2x00_async_login(vha, e->u.logio.fcport,
+ e->u.logio.data);
+ break;
+ case QLA_EVT_ASYNC_LOGIN_DONE:
+ qla2x00_async_login_done(vha, e->u.logio.fcport,
+ e->u.logio.data);
+ break;
+ case QLA_EVT_ASYNC_LOGOUT:
+ qla2x00_async_logout(vha, e->u.logio.fcport);
+ break;
+ case QLA_EVT_ASYNC_LOGOUT_DONE:
+ qla2x00_async_logout_done(vha, e->u.logio.fcport,
+ e->u.logio.data);
+ break;
}
if (e->flags & QLA_EVT_FLAG_FREE)
kfree(e);
@@ -2635,6 +2705,7 @@ void qla2x00_relogin(struct scsi_qla_host *vha)
int status;
uint16_t next_loopid = 0;
struct qla_hw_data *ha = vha->hw;
+ uint16_t data[2];
list_for_each_entry(fcport, &vha->vp_fcports, list) {
/*
@@ -2644,6 +2715,7 @@ void qla2x00_relogin(struct scsi_qla_host *vha)
if (atomic_read(&fcport->state) !=
FCS_ONLINE && fcport->login_retry) {
+ fcport->login_retry--;
if (fcport->flags & FCF_FABRIC_DEVICE) {
if (fcport->flags & FCF_TAPE_PRESENT)
ha->isp_ops->fabric_logout(vha,
@@ -2652,13 +2724,22 @@ void qla2x00_relogin(struct scsi_qla_host *vha)
fcport->d_id.b.area,
fcport->d_id.b.al_pa);
- status = qla2x00_fabric_login(vha, fcport,
- &next_loopid);
+ if (IS_ALOGIO_CAPABLE(ha)) {
+ data[0] = 0;
+ data[1] = QLA_LOGIO_LOGIN_RETRIED;
+ status = qla2x00_post_async_login_work(
+ vha, fcport, data);
+ if (status == QLA_SUCCESS)
+ continue;
+ /* Attempt a retry. */
+ status = 1;
+ } else
+ status = qla2x00_fabric_login(vha,
+ fcport, &next_loopid);
} else
status = qla2x00_local_device_login(vha,
fcport);
- fcport->login_retry--;
if (status == QLA_SUCCESS) {
fcport->old_loop_id = fcport->loop_id;
@@ -2831,6 +2912,9 @@ qla2x00_do_dpc(void *data)
*/
ha->dpc_active = 0;
+ /* Cleanup any residual CTX SRBs. */
+ qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16);
+
return 0;
}
@@ -2971,6 +3055,8 @@ qla2x00_timer(scsi_qla_host_t *vha)
sp = req->outstanding_cmds[index];
if (!sp)
continue;
+ if (sp->ctx)
+ continue;
sfcp = sp->fcport;
if (!(sfcp->flags & FCF_TAPE_PRESENT))
continue;
@@ -2987,8 +3073,7 @@ qla2x00_timer(scsi_qla_host_t *vha)
/* if the loop has been down for 4 minutes, reinit adapter */
if (atomic_dec_and_test(&vha->loop_down_timer) != 0) {
- if (!(vha->device_flags & DFLG_NO_CABLE) &&
- !vha->vp_idx) {
+ if (!(vha->device_flags & DFLG_NO_CABLE)) {
DEBUG(printk("scsi(%ld): Loop down - "
"aborting ISP.\n",
vha->host_no));
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
index 84369705a9a..ac107a2c34a 100644
--- a/drivers/scsi/qla2xxx/qla_version.h
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -7,7 +7,7 @@
/*
* Driver version
*/
-#define QLA2XXX_VERSION "8.03.01-k4"
+#define QLA2XXX_VERSION "8.03.01-k6"
#define QLA_DRIVER_MAJOR_VER 8
#define QLA_DRIVER_MINOR_VER 3
diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c
index 8025ee16588..c196d55eae3 100644
--- a/drivers/scsi/qla4xxx/ql4_isr.c
+++ b/drivers/scsi/qla4xxx/ql4_isr.c
@@ -227,11 +227,11 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
case SCS_DATA_UNDERRUN:
case SCS_DATA_OVERRUN:
if ((sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) ||
- (sts_entry->completionStatus == SCS_DATA_OVERRUN)) {
- DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " "Data overrun, "
- "residual = 0x%x\n", ha->host_no,
+ (sts_entry->completionStatus == SCS_DATA_OVERRUN)) {
+ DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " "Data overrun\n",
+ ha->host_no,
cmd->device->channel, cmd->device->id,
- cmd->device->lun, __func__, residual));
+ cmd->device->lun, __func__));
cmd->result = DID_ERROR << 16;
break;
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 2de5f3ad640..b6e03074cb8 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -994,7 +994,7 @@ static int scsi_vpd_inquiry(struct scsi_device *sdev, unsigned char *buffer,
* all the existing users tried this hard.
*/
result = scsi_execute_req(sdev, cmd, DMA_FROM_DEVICE, buffer,
- len + 4, NULL, 30 * HZ, 3, NULL);
+ len, NULL, 30 * HZ, 3, NULL);
if (result)
return result;
@@ -1021,13 +1021,14 @@ unsigned char *scsi_get_vpd_page(struct scsi_device *sdev, u8 page)
{
int i, result;
unsigned int len;
- unsigned char *buf = kmalloc(259, GFP_KERNEL);
+ const unsigned int init_vpd_len = 255;
+ unsigned char *buf = kmalloc(init_vpd_len, GFP_KERNEL);
if (!buf)
return NULL;
/* Ask for all the pages supported by this device */
- result = scsi_vpd_inquiry(sdev, buf, 0, 255);
+ result = scsi_vpd_inquiry(sdev, buf, 0, init_vpd_len);
if (result)
goto fail;
@@ -1050,12 +1051,12 @@ unsigned char *scsi_get_vpd_page(struct scsi_device *sdev, u8 page)
* Some pages are longer than 255 bytes. The actual length of
* the page is returned in the header.
*/
- len = (buf[2] << 8) | buf[3];
- if (len <= 255)
+ len = ((buf[2] << 8) | buf[3]) + 4;
+ if (len <= init_vpd_len)
return buf;
kfree(buf);
- buf = kmalloc(len + 4, GFP_KERNEL);
+ buf = kmalloc(len, GFP_KERNEL);
result = scsi_vpd_inquiry(sdev, buf, page, len);
if (result)
goto fail;
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index a1689353d7f..877204daf54 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -382,9 +382,13 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd)
* who knows? FIXME(eric)
*/
return SUCCESS;
+ case RESERVATION_CONFLICT:
+ /*
+ * let issuer deal with this, it could be just fine
+ */
+ return SUCCESS;
case BUSY:
case QUEUE_FULL:
- case RESERVATION_CONFLICT:
default:
return FAILED;
}
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index f3c40898fc7..5987da85710 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -896,9 +896,12 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
scsi_print_result(cmd);
if (driver_byte(result) & DRIVER_SENSE)
scsi_print_sense("", cmd);
+ scsi_print_command(cmd);
}
- blk_end_request_all(req, -EIO);
- scsi_next_command(cmd);
+ if (blk_end_request_err(req, -EIO))
+ scsi_requeue_command(q, cmd);
+ else
+ scsi_next_command(cmd);
break;
case ACTION_REPREP:
/* Unprep the request and put it back at the head of the queue.
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 292c02f810d..b98885de687 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -291,7 +291,7 @@ static void fc_scsi_scan_rport(struct work_struct *work);
#define FC_STARGET_NUM_ATTRS 3
#define FC_RPORT_NUM_ATTRS 10
#define FC_VPORT_NUM_ATTRS 9
-#define FC_HOST_NUM_ATTRS 21
+#define FC_HOST_NUM_ATTRS 22
struct fc_internal {
struct scsi_transport_template t;
@@ -3432,7 +3432,7 @@ fc_bsg_jobdone(struct fc_bsg_job *job)
/**
* fc_bsg_softirq_done - softirq done routine for destroying the bsg requests
- * @req: BSG request that holds the job to be destroyed
+ * @rq: BSG request that holds the job to be destroyed
*/
static void fc_bsg_softirq_done(struct request *rq)
{
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index b47240ca4b1..ad897df3661 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -36,6 +36,38 @@
#define ISCSI_TRANSPORT_VERSION "2.0-870"
+static int dbg_session;
+module_param_named(debug_session, dbg_session, int,
+ S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug_session,
+ "Turn on debugging for sessions in scsi_transport_iscsi "
+ "module. Set to 1 to turn on, and zero to turn off. Default "
+ "is off.");
+
+static int dbg_conn;
+module_param_named(debug_conn, dbg_conn, int,
+ S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug_conn,
+ "Turn on debugging for connections in scsi_transport_iscsi "
+ "module. Set to 1 to turn on, and zero to turn off. Default "
+ "is off.");
+
+#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...) \
+ do { \
+ if (dbg_session) \
+ iscsi_cls_session_printk(KERN_INFO, _session, \
+ "%s: " dbg_fmt, \
+ __func__, ##arg); \
+ } while (0);
+
+#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...) \
+ do { \
+ if (dbg_conn) \
+ iscsi_cls_conn_printk(KERN_INFO, _conn, \
+ "%s: " dbg_fmt, \
+ __func__, ##arg); \
+ } while (0);
+
struct iscsi_internal {
struct scsi_transport_template t;
struct iscsi_transport *iscsi_transport;
@@ -377,6 +409,7 @@ static void iscsi_session_release(struct device *dev)
shost = iscsi_session_to_shost(session);
scsi_host_put(shost);
+ ISCSI_DBG_TRANS_SESSION(session, "Completing session release\n");
kfree(session);
}
@@ -441,6 +474,9 @@ static int iscsi_user_scan_session(struct device *dev, void *data)
return 0;
session = iscsi_dev_to_session(dev);
+
+ ISCSI_DBG_TRANS_SESSION(session, "Scanning session\n");
+
shost = iscsi_session_to_shost(session);
ihost = shost->shost_data;
@@ -448,8 +484,7 @@ static int iscsi_user_scan_session(struct device *dev, void *data)
spin_lock_irqsave(&session->lock, flags);
if (session->state != ISCSI_SESSION_LOGGED_IN) {
spin_unlock_irqrestore(&session->lock, flags);
- mutex_unlock(&ihost->mutex);
- return 0;
+ goto user_scan_exit;
}
id = session->target_id;
spin_unlock_irqrestore(&session->lock, flags);
@@ -462,7 +497,10 @@ static int iscsi_user_scan_session(struct device *dev, void *data)
scsi_scan_target(&session->dev, 0, id,
scan_data->lun, 1);
}
+
+user_scan_exit:
mutex_unlock(&ihost->mutex);
+ ISCSI_DBG_TRANS_SESSION(session, "Completed session scan\n");
return 0;
}
@@ -522,7 +560,9 @@ static void session_recovery_timedout(struct work_struct *work)
if (session->transport->session_recovery_timedout)
session->transport->session_recovery_timedout(session);
+ ISCSI_DBG_TRANS_SESSION(session, "Unblocking SCSI target\n");
scsi_target_unblock(&session->dev);
+ ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking SCSI target\n");
}
static void __iscsi_unblock_session(struct work_struct *work)
@@ -534,6 +574,7 @@ static void __iscsi_unblock_session(struct work_struct *work)
struct iscsi_cls_host *ihost = shost->shost_data;
unsigned long flags;
+ ISCSI_DBG_TRANS_SESSION(session, "Unblocking session\n");
/*
* The recovery and unblock work get run from the same workqueue,
* so try to cancel it if it was going to run after this unblock.
@@ -553,6 +594,7 @@ static void __iscsi_unblock_session(struct work_struct *work)
if (scsi_queue_work(shost, &session->scan_work))
atomic_inc(&ihost->nr_scans);
}
+ ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking session\n");
}
/**
@@ -579,10 +621,12 @@ static void __iscsi_block_session(struct work_struct *work)
block_work);
unsigned long flags;
+ ISCSI_DBG_TRANS_SESSION(session, "Blocking session\n");
spin_lock_irqsave(&session->lock, flags);
session->state = ISCSI_SESSION_FAILED;
spin_unlock_irqrestore(&session->lock, flags);
scsi_target_block(&session->dev);
+ ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n");
queue_delayed_work(iscsi_eh_timer_workq, &session->recovery_work,
session->recovery_tmo * HZ);
}
@@ -602,6 +646,8 @@ static void __iscsi_unbind_session(struct work_struct *work)
struct iscsi_cls_host *ihost = shost->shost_data;
unsigned long flags;
+ ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n");
+
/* Prevent new scans and make sure scanning is not in progress */
mutex_lock(&ihost->mutex);
spin_lock_irqsave(&session->lock, flags);
@@ -616,6 +662,7 @@ static void __iscsi_unbind_session(struct work_struct *work)
scsi_remove_target(&session->dev);
iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
+ ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n");
}
struct iscsi_cls_session *
@@ -647,6 +694,8 @@ iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
device_initialize(&session->dev);
if (dd_size)
session->dd_data = &session[1];
+
+ ISCSI_DBG_TRANS_SESSION(session, "Completed session allocation\n");
return session;
}
EXPORT_SYMBOL_GPL(iscsi_alloc_session);
@@ -712,6 +761,7 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
spin_unlock_irqrestore(&sesslock, flags);
iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
+ ISCSI_DBG_TRANS_SESSION(session, "Completed session adding\n");
return 0;
release_host:
@@ -752,6 +802,7 @@ static void iscsi_conn_release(struct device *dev)
struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev);
struct device *parent = conn->dev.parent;
+ ISCSI_DBG_TRANS_CONN(conn, "Releasing conn\n");
kfree(conn);
put_device(parent);
}
@@ -774,6 +825,8 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
unsigned long flags;
int err;
+ ISCSI_DBG_TRANS_SESSION(session, "Removing session\n");
+
spin_lock_irqsave(&sesslock, flags);
list_del(&session->sess_list);
spin_unlock_irqrestore(&sesslock, flags);
@@ -807,12 +860,15 @@ void iscsi_remove_session(struct iscsi_cls_session *session)
"for session. Error %d.\n", err);
transport_unregister_device(&session->dev);
+
+ ISCSI_DBG_TRANS_SESSION(session, "Completing session removal\n");
device_del(&session->dev);
}
EXPORT_SYMBOL_GPL(iscsi_remove_session);
void iscsi_free_session(struct iscsi_cls_session *session)
{
+ ISCSI_DBG_TRANS_SESSION(session, "Freeing session\n");
iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION);
put_device(&session->dev);
}
@@ -828,6 +884,7 @@ EXPORT_SYMBOL_GPL(iscsi_free_session);
int iscsi_destroy_session(struct iscsi_cls_session *session)
{
iscsi_remove_session(session);
+ ISCSI_DBG_TRANS_SESSION(session, "Completing session destruction\n");
iscsi_free_session(session);
return 0;
}
@@ -885,6 +942,8 @@ iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)
list_add(&conn->conn_list, &connlist);
conn->active = 1;
spin_unlock_irqrestore(&connlock, flags);
+
+ ISCSI_DBG_TRANS_CONN(conn, "Completed conn creation\n");
return conn;
release_parent_ref:
@@ -912,6 +971,7 @@ int iscsi_destroy_conn(struct iscsi_cls_conn *conn)
spin_unlock_irqrestore(&connlock, flags);
transport_unregister_device(&conn->dev);
+ ISCSI_DBG_TRANS_CONN(conn, "Completing conn destruction\n");
device_unregister(&conn->dev);
return 0;
}
@@ -1200,6 +1260,9 @@ int iscsi_session_event(struct iscsi_cls_session *session,
"Cannot notify userspace of session "
"event %u. Check iscsi daemon\n",
event);
+
+ ISCSI_DBG_TRANS_SESSION(session, "Completed handling event %d rc %d\n",
+ event, rc);
return rc;
}
EXPORT_SYMBOL_GPL(iscsi_session_event);
@@ -1221,6 +1284,8 @@ iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep,
shost = iscsi_session_to_shost(session);
ev->r.c_session_ret.host_no = shost->host_no;
ev->r.c_session_ret.sid = session->sid;
+ ISCSI_DBG_TRANS_SESSION(session,
+ "Completed creating transport session\n");
return 0;
}
@@ -1246,6 +1311,8 @@ iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
ev->r.c_conn_ret.sid = session->sid;
ev->r.c_conn_ret.cid = conn->cid;
+
+ ISCSI_DBG_TRANS_CONN(conn, "Completed creating transport conn\n");
return 0;
}
@@ -1258,8 +1325,10 @@ iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev
if (!conn)
return -EINVAL;
+ ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n");
if (transport->destroy_conn)
transport->destroy_conn(conn);
+
return 0;
}
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c
index 0895d3c71b0..fd47cb1bee1 100644
--- a/drivers/scsi/scsi_transport_sas.c
+++ b/drivers/scsi/scsi_transport_sas.c
@@ -1692,10 +1692,6 @@ sas_attach_transport(struct sas_function_template *ft)
i->f = ft;
count = 0;
- SETUP_PORT_ATTRIBUTE(num_phys);
- i->host_attrs[count] = NULL;
-
- count = 0;
SETUP_PHY_ATTRIBUTE(initiator_port_protocols);
SETUP_PHY_ATTRIBUTE(target_port_protocols);
SETUP_PHY_ATTRIBUTE(device_type);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index b7b9fec67a9..a89c421dab5 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -2021,6 +2021,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
sdp->removable ? "removable " : "");
+ put_device(&sdkp->dev);
}
/**
@@ -2106,6 +2107,7 @@ static int sd_probe(struct device *dev)
get_device(&sdp->sdev_gendev);
+ get_device(&sdkp->dev); /* prevent release before async_schedule */
async_schedule(sd_probe_async, sdkp);
return 0;
diff --git a/drivers/scsi/ses.c b/drivers/scsi/ses.c
index 4f618f48735..55b034b7270 100644
--- a/drivers/scsi/ses.c
+++ b/drivers/scsi/ses.c
@@ -347,6 +347,97 @@ static int ses_enclosure_find_by_addr(struct enclosure_device *edev,
return 0;
}
+#define INIT_ALLOC_SIZE 32
+
+static void ses_enclosure_data_process(struct enclosure_device *edev,
+ struct scsi_device *sdev,
+ int create)
+{
+ u32 result;
+ unsigned char *buf = NULL, *type_ptr, *desc_ptr, *addl_desc_ptr = NULL;
+ int i, j, page7_len, len, components;
+ struct ses_device *ses_dev = edev->scratch;
+ int types = ses_dev->page1[10];
+ unsigned char *hdr_buf = kzalloc(INIT_ALLOC_SIZE, GFP_KERNEL);
+
+ if (!hdr_buf)
+ goto simple_populate;
+
+ /* re-read page 10 */
+ if (ses_dev->page10)
+ ses_recv_diag(sdev, 10, ses_dev->page10, ses_dev->page10_len);
+ /* Page 7 for the descriptors is optional */
+ result = ses_recv_diag(sdev, 7, hdr_buf, INIT_ALLOC_SIZE);
+ if (result)
+ goto simple_populate;
+
+ page7_len = len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
+ /* add 1 for trailing '\0' we'll use */
+ buf = kzalloc(len + 1, GFP_KERNEL);
+ if (!buf)
+ goto simple_populate;
+ result = ses_recv_diag(sdev, 7, buf, len);
+ if (result) {
+ simple_populate:
+ kfree(buf);
+ buf = NULL;
+ desc_ptr = NULL;
+ len = 0;
+ page7_len = 0;
+ } else {
+ desc_ptr = buf + 8;
+ len = (desc_ptr[2] << 8) + desc_ptr[3];
+ /* skip past overall descriptor */
+ desc_ptr += len + 4;
+ if (ses_dev->page10)
+ addl_desc_ptr = ses_dev->page10 + 8;
+ }
+ type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
+ components = 0;
+ for (i = 0; i < types; i++, type_ptr += 4) {
+ for (j = 0; j < type_ptr[1]; j++) {
+ char *name = NULL;
+ struct enclosure_component *ecomp;
+
+ if (desc_ptr) {
+ if (desc_ptr >= buf + page7_len) {
+ desc_ptr = NULL;
+ } else {
+ len = (desc_ptr[2] << 8) + desc_ptr[3];
+ desc_ptr += 4;
+ /* Add trailing zero - pushes into
+ * reserved space */
+ desc_ptr[len] = '\0';
+ name = desc_ptr;
+ }
+ }
+ if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
+ type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) {
+
+ if (create)
+ ecomp = enclosure_component_register(edev,
+ components++,
+ type_ptr[0],
+ name);
+ else
+ ecomp = &edev->component[components++];
+
+ if (!IS_ERR(ecomp) && addl_desc_ptr)
+ ses_process_descriptor(ecomp,
+ addl_desc_ptr);
+ }
+ if (desc_ptr)
+ desc_ptr += len;
+
+ if (addl_desc_ptr)
+ addl_desc_ptr += addl_desc_ptr[1] + 2;
+
+ }
+ }
+ kfree(buf);
+ kfree(hdr_buf);
+}
+
static void ses_match_to_enclosure(struct enclosure_device *edev,
struct scsi_device *sdev)
{
@@ -361,6 +452,8 @@ static void ses_match_to_enclosure(struct enclosure_device *edev,
if (!buf)
return;
+ ses_enclosure_data_process(edev, to_scsi_device(edev->edev.parent), 0);
+
vpd_len = ((buf[2] << 8) | buf[3]) + 4;
desc = buf + 4;
@@ -395,28 +488,26 @@ static void ses_match_to_enclosure(struct enclosure_device *edev,
kfree(buf);
}
-#define INIT_ALLOC_SIZE 32
-
static int ses_intf_add(struct device *cdev,
struct class_interface *intf)
{
struct scsi_device *sdev = to_scsi_device(cdev->parent);
struct scsi_device *tmp_sdev;
- unsigned char *buf = NULL, *hdr_buf, *type_ptr, *desc_ptr = NULL,
- *addl_desc_ptr = NULL;
+ unsigned char *buf = NULL, *hdr_buf, *type_ptr;
struct ses_device *ses_dev;
u32 result;
- int i, j, types, len, page7_len = 0, components = 0;
+ int i, types, len, components = 0;
int err = -ENOMEM;
struct enclosure_device *edev;
struct ses_component *scomp = NULL;
if (!scsi_device_enclosure(sdev)) {
/* not an enclosure, but might be in one */
- edev = enclosure_find(&sdev->host->shost_gendev);
- if (edev) {
+ struct enclosure_device *prev = NULL;
+
+ while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) {
ses_match_to_enclosure(edev, sdev);
- put_device(&edev->edev);
+ prev = edev;
}
return -ENODEV;
}
@@ -500,6 +591,7 @@ static int ses_intf_add(struct device *cdev,
ses_dev->page10_len = len;
buf = NULL;
}
+ kfree(hdr_buf);
scomp = kzalloc(sizeof(struct ses_component) * components, GFP_KERNEL);
if (!scomp)
@@ -516,72 +608,7 @@ static int ses_intf_add(struct device *cdev,
for (i = 0; i < components; i++)
edev->component[i].scratch = scomp + i;
- /* Page 7 for the descriptors is optional */
- result = ses_recv_diag(sdev, 7, hdr_buf, INIT_ALLOC_SIZE);
- if (result)
- goto simple_populate;
-
- page7_len = len = (hdr_buf[2] << 8) + hdr_buf[3] + 4;
- /* add 1 for trailing '\0' we'll use */
- buf = kzalloc(len + 1, GFP_KERNEL);
- if (!buf)
- goto simple_populate;
- result = ses_recv_diag(sdev, 7, buf, len);
- if (result) {
- simple_populate:
- kfree(buf);
- buf = NULL;
- desc_ptr = NULL;
- addl_desc_ptr = NULL;
- } else {
- desc_ptr = buf + 8;
- len = (desc_ptr[2] << 8) + desc_ptr[3];
- /* skip past overall descriptor */
- desc_ptr += len + 4;
- if (ses_dev->page10)
- addl_desc_ptr = ses_dev->page10 + 8;
- }
- type_ptr = ses_dev->page1 + 12 + ses_dev->page1[11];
- components = 0;
- for (i = 0; i < types; i++, type_ptr += 4) {
- for (j = 0; j < type_ptr[1]; j++) {
- char *name = NULL;
- struct enclosure_component *ecomp;
-
- if (desc_ptr) {
- if (desc_ptr >= buf + page7_len) {
- desc_ptr = NULL;
- } else {
- len = (desc_ptr[2] << 8) + desc_ptr[3];
- desc_ptr += 4;
- /* Add trailing zero - pushes into
- * reserved space */
- desc_ptr[len] = '\0';
- name = desc_ptr;
- }
- }
- if (type_ptr[0] == ENCLOSURE_COMPONENT_DEVICE ||
- type_ptr[0] == ENCLOSURE_COMPONENT_ARRAY_DEVICE) {
-
- ecomp = enclosure_component_register(edev,
- components++,
- type_ptr[0],
- name);
-
- if (!IS_ERR(ecomp) && addl_desc_ptr)
- ses_process_descriptor(ecomp,
- addl_desc_ptr);
- }
- if (desc_ptr)
- desc_ptr += len;
-
- if (addl_desc_ptr)
- addl_desc_ptr += addl_desc_ptr[1] + 2;
-
- }
- }
- kfree(buf);
- kfree(hdr_buf);
+ ses_enclosure_data_process(edev, sdev, 1);
/* see if there are any devices matching before
* we found the enclosure */
@@ -615,17 +642,26 @@ static int ses_remove(struct device *dev)
return 0;
}
-static void ses_intf_remove(struct device *cdev,
- struct class_interface *intf)
+static void ses_intf_remove_component(struct scsi_device *sdev)
+{
+ struct enclosure_device *edev, *prev = NULL;
+
+ while ((edev = enclosure_find(&sdev->host->shost_gendev, prev)) != NULL) {
+ prev = edev;
+ if (!enclosure_remove_device(edev, &sdev->sdev_gendev))
+ break;
+ }
+ if (edev)
+ put_device(&edev->edev);
+}
+
+static void ses_intf_remove_enclosure(struct scsi_device *sdev)
{
- struct scsi_device *sdev = to_scsi_device(cdev->parent);
struct enclosure_device *edev;
struct ses_device *ses_dev;
- if (!scsi_device_enclosure(sdev))
- return;
-
- edev = enclosure_find(cdev->parent);
+ /* exact match to this enclosure */
+ edev = enclosure_find(&sdev->sdev_gendev, NULL);
if (!edev)
return;
@@ -643,6 +679,17 @@ static void ses_intf_remove(struct device *cdev,
enclosure_unregister(edev);
}
+static void ses_intf_remove(struct device *cdev,
+ struct class_interface *intf)
+{
+ struct scsi_device *sdev = to_scsi_device(cdev->parent);
+
+ if (!scsi_device_enclosure(sdev))
+ ses_intf_remove_component(sdev);
+ else
+ ses_intf_remove_enclosure(sdev);
+}
+
static struct class_interface ses_interface = {
.add_dev = ses_intf_add,
.remove_dev = ses_intf_remove,
diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c
index 9230402c45a..4968c4ced38 100644
--- a/drivers/scsi/sg.c
+++ b/drivers/scsi/sg.c
@@ -1811,7 +1811,7 @@ retry:
return 0;
out:
for (i = 0; i < k; i++)
- __free_pages(schp->pages[k], order);
+ __free_pages(schp->pages[i], order);
if (--order >= 0)
goto retry;
diff --git a/drivers/scsi/stex.c b/drivers/scsi/stex.c
index 8d2a95c4e5b..09fa8861fc5 100644
--- a/drivers/scsi/stex.c
+++ b/drivers/scsi/stex.c
@@ -55,6 +55,7 @@ enum {
OIS = 0x30, /* MU_OUTBOUND_INTERRUPT_STATUS */
OIM = 0x3c, /* MU_OUTBOUND_INTERRUPT_MASK */
+ YIOA_STATUS = 0x00,
YH2I_INT = 0x20,
YINT_EN = 0x34,
YI2H_INT = 0x9c,
@@ -108,6 +109,10 @@ enum {
SS_HEAD_HANDSHAKE = 0x80,
+ SS_H2I_INT_RESET = 0x100,
+
+ SS_MU_OPERATIONAL = 0x80000000,
+
STEX_CDB_LENGTH = 16,
STATUS_VAR_LEN = 128,
@@ -884,7 +889,7 @@ static void stex_ss_mu_intr(struct st_hba *hba)
tag = (u16)value;
if (unlikely(tag >= hba->host->can_queue)) {
printk(KERN_WARNING DRV_NAME
- "(%s): invalid tag\n", pci_name(hba->pdev));
+ "(%s): invalid tag\n", pci_name(hba->pdev));
continue;
}
@@ -1040,16 +1045,27 @@ static int stex_ss_handshake(struct st_hba *hba)
void __iomem *base = hba->mmio_base;
struct st_msg_header *msg_h;
struct handshake_frame *h;
- __le32 *scratch = hba->scratch;
+ __le32 *scratch;
u32 data;
unsigned long before;
int ret = 0;
- h = (struct handshake_frame *)(hba->alloc_rq(hba));
- msg_h = (struct st_msg_header *)h - 1;
+ before = jiffies;
+ while ((readl(base + YIOA_STATUS) & SS_MU_OPERATIONAL) == 0) {
+ if (time_after(jiffies, before + MU_MAX_DELAY * HZ)) {
+ printk(KERN_ERR DRV_NAME
+ "(%s): firmware not operational\n",
+ pci_name(hba->pdev));
+ return -1;
+ }
+ msleep(1);
+ }
+
+ msg_h = (struct st_msg_header *)hba->dma_mem;
msg_h->handle = cpu_to_le64(hba->dma_handle);
msg_h->flag = SS_HEAD_HANDSHAKE;
+ h = (struct handshake_frame *)(msg_h + 1);
h->rb_phy = cpu_to_le64(hba->dma_handle);
h->req_sz = cpu_to_le16(hba->rq_size);
h->req_cnt = cpu_to_le16(hba->rq_count+1);
@@ -1205,6 +1221,13 @@ static void stex_hard_reset(struct st_hba *hba)
hba->pdev->saved_config_space[i]);
}
+static void stex_ss_reset(struct st_hba *hba)
+{
+ writel(SS_H2I_INT_RESET, hba->mmio_base + YH2I_INT);
+ readl(hba->mmio_base + YH2I_INT);
+ ssleep(5);
+}
+
static int stex_reset(struct scsi_cmnd *cmd)
{
struct st_hba *hba;
@@ -1221,6 +1244,8 @@ static int stex_reset(struct scsi_cmnd *cmd)
if (hba->cardtype == st_shasta)
stex_hard_reset(hba);
+ else if (hba->cardtype == st_yel)
+ stex_ss_reset(hba);
if (hba->cardtype != st_yosemite) {
if (stex_handshake(hba)) {
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 6553833c12d..03422ce878c 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -459,7 +459,7 @@ config SERIAL_SAMSUNG_UARTS
int
depends on ARM && PLAT_S3C
default 2 if ARCH_S3C2400
- default 4 if ARCH_S3C64XX || CPU_S3C2443
+ default 4 if ARCH_S5PC1XX || ARCH_S3C64XX || CPU_S3C2443
default 3
help
Select the number of available UART ports for the Samsung S3C
@@ -533,6 +533,13 @@ config SERIAL_S3C6400
Serial port support for the Samsung S3C6400 and S3C6410
SoCs
+config SERIAL_S5PC100
+ tristate "Samsung S5PC100 Serial port support"
+ depends on SERIAL_SAMSUNG && CPU_S5PC100
+ default y
+ help
+ Serial port support for the Samsung S5PC100 SoCs
+
config SERIAL_MAX3100
tristate "MAX3100 support"
depends on SPI
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index d5a29981c6c..97f6fcc8b43 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_SERIAL_S3C2412) += s3c2412.o
obj-$(CONFIG_SERIAL_S3C2440) += s3c2440.o
obj-$(CONFIG_SERIAL_S3C24A0) += s3c24a0.o
obj-$(CONFIG_SERIAL_S3C6400) += s3c6400.o
+obj-$(CONFIG_SERIAL_S5PC100) += s3c6400.o
obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
obj-$(CONFIG_SERIAL_MUX) += mux.o
diff --git a/drivers/serial/amba-pl011.c b/drivers/serial/amba-pl011.c
index bf82e28770a..72ba0c6d355 100644
--- a/drivers/serial/amba-pl011.c
+++ b/drivers/serial/amba-pl011.c
@@ -826,6 +826,28 @@ static int pl011_remove(struct amba_device *dev)
return 0;
}
+#ifdef CONFIG_PM
+static int pl011_suspend(struct amba_device *dev, pm_message_t state)
+{
+ struct uart_amba_port *uap = amba_get_drvdata(dev);
+
+ if (!uap)
+ return -EINVAL;
+
+ return uart_suspend_port(&amba_reg, &uap->port);
+}
+
+static int pl011_resume(struct amba_device *dev)
+{
+ struct uart_amba_port *uap = amba_get_drvdata(dev);
+
+ if (!uap)
+ return -EINVAL;
+
+ return uart_resume_port(&amba_reg, &uap->port);
+}
+#endif
+
static struct amba_id pl011_ids[] __initdata = {
{
.id = 0x00041011,
@@ -847,6 +869,10 @@ static struct amba_driver pl011_driver = {
.id_table = pl011_ids,
.probe = pl011_probe,
.remove = pl011_remove,
+#ifdef CONFIG_PM
+ .suspend = pl011_suspend,
+ .resume = pl011_resume,
+#endif
};
static int __init pl011_init(void)
diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c
index 5d7b58f1fe4..7485afd0df4 100644
--- a/drivers/serial/imx.c
+++ b/drivers/serial/imx.c
@@ -67,21 +67,8 @@
#define UBIR 0xa4 /* BRM Incremental Register */
#define UBMR 0xa8 /* BRM Modulator Register */
#define UBRC 0xac /* Baud Rate Count Register */
-#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
-#define ONEMS 0xb0 /* One Millisecond register */
-#define UTS 0xb4 /* UART Test Register */
-#endif
-#ifdef CONFIG_ARCH_MX1
-#define BIPR1 0xb0 /* Incremental Preset Register 1 */
-#define BIPR2 0xb4 /* Incremental Preset Register 2 */
-#define BIPR3 0xb8 /* Incremental Preset Register 3 */
-#define BIPR4 0xbc /* Incremental Preset Register 4 */
-#define BMPR1 0xc0 /* BRM Modulator Register 1 */
-#define BMPR2 0xc4 /* BRM Modulator Register 2 */
-#define BMPR3 0xc8 /* BRM Modulator Register 3 */
-#define BMPR4 0xcc /* BRM Modulator Register 4 */
-#define UTS 0xd0 /* UART Test Register */
-#endif
+#define MX2_ONEMS 0xb0 /* One Millisecond register */
+#define UTS (cpu_is_mx1() ? 0xd0 : 0xb4) /* UART Test Register */
/* UART Control Register Bit Fields.*/
#define URXD_CHARRDY (1<<15)
@@ -101,12 +88,7 @@
#define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */
#define UCR1_SNDBRK (1<<4) /* Send break */
#define UCR1_TDMAEN (1<<3) /* Transmitter ready DMA enable */
-#ifdef CONFIG_ARCH_MX1
-#define UCR1_UARTCLKEN (1<<2) /* UART clock enabled */
-#endif
-#if defined CONFIG_ARCH_MX3 || defined CONFIG_ARCH_MX2
-#define UCR1_UARTCLKEN (0) /* not present on mx2/mx3 */
-#endif
+#define MX1_UCR1_UARTCLKEN (1<<2) /* UART clock enabled, mx1 only */
#define UCR1_DOZE (1<<1) /* Doze */
#define UCR1_UARTEN (1<<0) /* UART enabled */
#define UCR2_ESCI (1<<15) /* Escape seq interrupt enable */
@@ -132,13 +114,9 @@
#define UCR3_RXDSEN (1<<6) /* Receive status interrupt enable */
#define UCR3_AIRINTEN (1<<5) /* Async IR wake interrupt enable */
#define UCR3_AWAKEN (1<<4) /* Async wake interrupt enable */
-#ifdef CONFIG_ARCH_MX1
-#define UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */
-#define UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */
-#endif
-#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3
-#define UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */
-#endif
+#define MX1_UCR3_REF25 (1<<3) /* Ref freq 25 MHz, only on mx1 */
+#define MX1_UCR3_REF30 (1<<2) /* Ref Freq 30 MHz, only on mx1 */
+#define MX2_UCR3_RXDMUXSEL (1<<2) /* RXD Muxed Input Select, on mx2/mx3 */
#define UCR3_INVT (1<<1) /* Inverted Infrared transmission */
#define UCR3_BPEN (1<<0) /* Preset registers enable */
#define UCR4_CTSTL_32 (32<<10) /* CTS trigger level (32 chars) */
@@ -186,12 +164,10 @@
#define UTS_SOFTRST (1<<0) /* Software reset */
/* We've been assigned a range on the "Low-density serial ports" major */
-#ifdef CONFIG_ARCH_MXC
#define SERIAL_IMX_MAJOR 207
#define MINOR_START 16
#define DEV_NAME "ttymxc"
#define MAX_INTERNAL_IRQ MXC_INTERNAL_IRQS
-#endif
/*
* This determines how often we check the modem status signals
@@ -706,11 +682,11 @@ static int imx_startup(struct uart_port *port)
}
}
-#if defined CONFIG_ARCH_MX2 || defined CONFIG_ARCH_MX3
- temp = readl(sport->port.membase + UCR3);
- temp |= UCR3_RXDMUXSEL;
- writel(temp, sport->port.membase + UCR3);
-#endif
+ if (!cpu_is_mx1()) {
+ temp = readl(sport->port.membase + UCR3);
+ temp |= MX2_UCR3_RXDMUXSEL;
+ writel(temp, sport->port.membase + UCR3);
+ }
if (USE_IRDA(sport)) {
temp = readl(sport->port.membase + UCR4);
@@ -942,9 +918,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
writel(num, sport->port.membase + UBIR);
writel(denom, sport->port.membase + UBMR);
-#ifdef ONEMS
- writel(sport->port.uartclk / div / 1000, sport->port.membase + ONEMS);
-#endif
+ if (!cpu_is_mx1())
+ writel(sport->port.uartclk / div / 1000,
+ sport->port.membase + MX2_ONEMS);
writel(old_ucr1, sport->port.membase + UCR1);
@@ -1074,17 +1050,20 @@ static void
imx_console_write(struct console *co, const char *s, unsigned int count)
{
struct imx_port *sport = imx_ports[co->index];
- unsigned int old_ucr1, old_ucr2;
+ unsigned int old_ucr1, old_ucr2, ucr1;
/*
* First, save UCR1/2 and then disable interrupts
*/
- old_ucr1 = readl(sport->port.membase + UCR1);
+ ucr1 = old_ucr1 = readl(sport->port.membase + UCR1);
old_ucr2 = readl(sport->port.membase + UCR2);
- writel((old_ucr1 | UCR1_UARTCLKEN | UCR1_UARTEN) &
- ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN),
- sport->port.membase + UCR1);
+ if (cpu_is_mx1())
+ ucr1 |= MX1_UCR1_UARTCLKEN;
+ ucr1 |= UCR1_UARTEN;
+ ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN);
+
+ writel(ucr1, sport->port.membase + UCR1);
writel(old_ucr2 | UCR2_TXEN, sport->port.membase + UCR2);
diff --git a/drivers/serial/serial_ks8695.c b/drivers/serial/serial_ks8695.c
index e0665630e4d..52db5cc3f90 100644
--- a/drivers/serial/serial_ks8695.c
+++ b/drivers/serial/serial_ks8695.c
@@ -110,7 +110,11 @@ static struct console ks8695_console;
static void ks8695uart_stop_tx(struct uart_port *port)
{
if (tx_enabled(port)) {
- disable_irq(KS8695_IRQ_UART_TX);
+ /* use disable_irq_nosync() and not disable_irq() to avoid self
+ * imposed deadlock by not waiting for irq handler to end,
+ * since this ks8695uart_stop_tx() is called from interrupt context.
+ */
+ disable_irq_nosync(KS8695_IRQ_UART_TX);
tx_enable(port, 0);
}
}
diff --git a/drivers/spi/amba-pl022.c b/drivers/spi/amba-pl022.c
index da76797ce8b..c0f950a7cbe 100644
--- a/drivers/spi/amba-pl022.c
+++ b/drivers/spi/amba-pl022.c
@@ -38,14 +38,12 @@
#include <linux/interrupt.h>
#include <linux/spi/spi.h>
#include <linux/workqueue.h>
-#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl022.h>
#include <linux/io.h>
-#include <linux/delay.h>
/*
* This macro is used to define some register default values.
diff --git a/drivers/staging/dst/dcore.c b/drivers/staging/dst/dcore.c
index 84724187ec3..ac8577358ba 100644
--- a/drivers/staging/dst/dcore.c
+++ b/drivers/staging/dst/dcore.c
@@ -112,8 +112,9 @@ static int dst_request(struct request_queue *q, struct bio *bio)
* I worked with.
*
* Empty barriers are not allowed anyway, see 51fd77bd9f512
- * for example, although later it was changed to bio_discard()
- * only, which does not work in this case.
+ * for example, although later it was changed to
+ * bio_rw_flagged(bio, BIO_RW_DISCARD) only, which does not
+ * work in this case.
*/
//err = -EOPNOTSUPP;
err = 0;
diff --git a/drivers/staging/pohmelfs/inode.c b/drivers/staging/pohmelfs/inode.c
index e63c9bea6c5..d004a9ddddb 100644
--- a/drivers/staging/pohmelfs/inode.c
+++ b/drivers/staging/pohmelfs/inode.c
@@ -921,16 +921,16 @@ ssize_t pohmelfs_write(struct file *file, const char __user *buf,
if (ret)
goto err_out_unlock;
- ret = generic_file_aio_write_nolock(&kiocb, &iov, 1, pos);
+ ret = __generic_file_aio_write(&kiocb, &iov, 1, &kiocb.ki_pos);
*ppos = kiocb.ki_pos;
mutex_unlock(&inode->i_mutex);
WARN_ON(ret < 0);
- if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+ if (ret > 0) {
ssize_t err;
- err = sync_page_range(inode, mapping, pos, ret);
+ err = generic_write_sync(file, pos, ret);
if (err < 0)
ret = err;
WARN_ON(ret < 0);
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 3b54b394017..1999b183481 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -935,7 +935,7 @@ config FB_S1D13XXX
config FB_ATMEL
tristate "AT91/AT32 LCD Controller support"
- depends on FB && (ARCH_AT91SAM9261 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9 || AVR32)
+ depends on FB && (ARCH_AT91SAM9261 || ARCH_AT91SAM9G10 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 || ARCH_AT91CAP9 || AVR32)
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -951,7 +951,7 @@ config FB_INTSRAM
config FB_ATMEL_STN
bool "Use a STN display with AT91/AT32 LCD Controller"
- depends on FB_ATMEL && MACH_AT91SAM9261EK
+ depends on FB_ATMEL && (MACH_AT91SAM9261EK || MACH_AT91SAM9G10EK)
default n
help
Say Y if you want to connect a STN LCD display to the AT91/AT32 LCD
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index da05f0801bb..2830ffd7297 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -182,7 +182,8 @@ static unsigned long compute_hozval(unsigned long xres, unsigned long lcdcon2)
{
unsigned long value;
- if (!(cpu_is_at91sam9261() || cpu_is_at32ap7000()))
+ if (!(cpu_is_at91sam9261() || cpu_is_at91sam9g10()
+ || cpu_is_at32ap7000()))
return xres;
value = xres;
@@ -824,7 +825,8 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev)
info->fix = atmel_lcdfb_fix;
/* Enable LCDC Clocks */
- if (cpu_is_at91sam9261() || cpu_is_at32ap7000()) {
+ if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()
+ || cpu_is_at32ap7000()) {
sinfo->bus_clk = clk_get(dev, "hck1");
if (IS_ERR(sinfo->bus_clk)) {
ret = PTR_ERR(sinfo->bus_clk);
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index f9d19be0554..90861cd9316 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -110,7 +110,7 @@ config BACKLIGHT_CLASS_DEVICE
config BACKLIGHT_ATMEL_LCDC
bool "Atmel LCDC Contrast-as-Backlight control"
depends on BACKLIGHT_CLASS_DEVICE && FB_ATMEL
- default y if MACH_SAM9261EK || MACH_SAM9263EK
+ default y if MACH_SAM9261EK || MACH_SAM9G10EK || MACH_SAM9263EK
help
This provides a backlight control internal to the Atmel LCDC
driver. If the LCD "contrast control" on your board is wired
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
index 15a0ee6d8e2..30ae3022f63 100644
--- a/drivers/video/imxfb.c
+++ b/drivers/video/imxfb.c
@@ -33,6 +33,7 @@
#include <linux/math64.h>
#include <mach/imxfb.h>
+#include <mach/hardware.h>
/*
* Complain if VAR is out of range.
@@ -129,6 +130,10 @@
#define LCDISR_EOF (1<<1)
#define LCDISR_BOF (1<<0)
+/* Used fb-mode. Can be set on kernel command line, therefore file-static. */
+static const char *fb_mode;
+
+
/*
* These are the bitfields for each
* display depth that we support.
@@ -145,10 +150,6 @@ struct imxfb_info {
void __iomem *regs;
struct clk *clk;
- u_int max_bpp;
- u_int max_xres;
- u_int max_yres;
-
/*
* These are the addresses we mapped
* the framebuffer memory region to.
@@ -172,6 +173,9 @@ struct imxfb_info {
cmap_static:1,
unused:30;
+ struct imx_fb_videomode *mode;
+ int num_modes;
+
void (*lcd_power)(int);
void (*backlight_power)(int);
};
@@ -298,6 +302,18 @@ static int imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
return ret;
}
+static const struct imx_fb_videomode *imxfb_find_mode(struct imxfb_info *fbi)
+{
+ struct imx_fb_videomode *m;
+ int i;
+
+ for (i = 0, m = &fbi->mode[0]; i < fbi->num_modes; i++, m++) {
+ if (!strcmp(m->mode.name, fb_mode))
+ return m;
+ }
+ return NULL;
+}
+
/*
* imxfb_check_var():
* Round up in the following order: bits_per_pixel, xres,
@@ -308,35 +324,81 @@ static int imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
struct imxfb_info *fbi = info->par;
struct imxfb_rgb *rgb;
+ const struct imx_fb_videomode *imxfb_mode;
+ unsigned long lcd_clk;
+ unsigned long long tmp;
+ u32 pcr = 0;
if (var->xres < MIN_XRES)
var->xres = MIN_XRES;
if (var->yres < MIN_YRES)
var->yres = MIN_YRES;
- if (var->xres > fbi->max_xres)
- var->xres = fbi->max_xres;
- if (var->yres > fbi->max_yres)
- var->yres = fbi->max_yres;
- var->xres_virtual = max(var->xres_virtual, var->xres);
- var->yres_virtual = max(var->yres_virtual, var->yres);
+
+ imxfb_mode = imxfb_find_mode(fbi);
+ if (!imxfb_mode)
+ return -EINVAL;
+
+ var->xres = imxfb_mode->mode.xres;
+ var->yres = imxfb_mode->mode.yres;
+ var->bits_per_pixel = imxfb_mode->bpp;
+ var->pixclock = imxfb_mode->mode.pixclock;
+ var->hsync_len = imxfb_mode->mode.hsync_len;
+ var->left_margin = imxfb_mode->mode.left_margin;
+ var->right_margin = imxfb_mode->mode.right_margin;
+ var->vsync_len = imxfb_mode->mode.vsync_len;
+ var->upper_margin = imxfb_mode->mode.upper_margin;
+ var->lower_margin = imxfb_mode->mode.lower_margin;
+ var->sync = imxfb_mode->mode.sync;
+ var->xres_virtual = max(var->xres_virtual, var->xres);
+ var->yres_virtual = max(var->yres_virtual, var->yres);
pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel);
+
+ lcd_clk = clk_get_rate(fbi->clk);
+
+ tmp = var->pixclock * (unsigned long long)lcd_clk;
+
+ do_div(tmp, 1000000);
+
+ if (do_div(tmp, 1000000) > 500000)
+ tmp++;
+
+ pcr = (unsigned int)tmp;
+
+ if (--pcr > 0x3F) {
+ pcr = 0x3F;
+ printk(KERN_WARNING "Must limit pixel clock to %luHz\n",
+ lcd_clk / pcr);
+ }
+
switch (var->bits_per_pixel) {
case 32:
+ pcr |= PCR_BPIX_18;
rgb = &def_rgb_18;
break;
case 16:
default:
- if (fbi->pcr & PCR_TFT)
+ if (cpu_is_mx1())
+ pcr |= PCR_BPIX_12;
+ else
+ pcr |= PCR_BPIX_16;
+
+ if (imxfb_mode->pcr & PCR_TFT)
rgb = &def_rgb_16_tft;
else
rgb = &def_rgb_16_stn;
break;
case 8:
+ pcr |= PCR_BPIX_8;
rgb = &def_rgb_8;
break;
}
+ /* add sync polarities */
+ pcr |= imxfb_mode->pcr & ~(0x3f | (7 << 25));
+
+ fbi->pcr = pcr;
+
/*
* Copy the RGB parameters for this display
* from the machine specific parameters.
@@ -393,10 +455,6 @@ static void imxfb_enable_controller(struct imxfb_info *fbi)
writel(fbi->screen_dma, fbi->regs + LCDC_SSA);
- /* physical screen start address */
- writel(VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4),
- fbi->regs + LCDC_VPW);
-
/* panning offset 0 (0 pixel offset) */
writel(0x00000000, fbi->regs + LCDC_POS);
@@ -468,8 +526,6 @@ static struct fb_ops imxfb_ops = {
static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
struct imxfb_info *fbi = info->par;
- unsigned int pcr, lcd_clk;
- unsigned long long tmp;
pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n",
var->xres, var->hsync_len,
@@ -505,6 +561,10 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
info->fix.id, var->lower_margin);
#endif
+ /* physical screen start address */
+ writel(VPW_VPW(var->xres * var->bits_per_pixel / 8 / 4),
+ fbi->regs + LCDC_VPW);
+
writel(HCR_H_WIDTH(var->hsync_len - 1) |
HCR_H_WAIT_1(var->right_margin - 1) |
HCR_H_WAIT_2(var->left_margin - 3),
@@ -518,22 +578,7 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
writel(SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres),
fbi->regs + LCDC_SIZE);
- lcd_clk = clk_get_rate(fbi->clk);
- tmp = var->pixclock * (unsigned long long)lcd_clk;
- do_div(tmp, 1000000);
- if (do_div(tmp, 1000000) > 500000)
- tmp++;
- pcr = (unsigned int)tmp;
- if (--pcr > 0x3F) {
- pcr = 0x3F;
- printk(KERN_WARNING "Must limit pixel clock to %uHz\n",
- lcd_clk / pcr);
- }
-
- /* add sync polarities */
- pcr |= fbi->pcr & ~0x3F;
-
- writel(pcr, fbi->regs + LCDC_PCR);
+ writel(fbi->pcr, fbi->regs + LCDC_PCR);
writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
@@ -575,6 +620,8 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev)
struct imx_fb_platform_data *pdata = pdev->dev.platform_data;
struct fb_info *info = dev_get_drvdata(&pdev->dev);
struct imxfb_info *fbi = info->par;
+ struct imx_fb_videomode *m;
+ int i;
pr_debug("%s\n",__func__);
@@ -603,35 +650,18 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev)
info->fbops = &imxfb_ops;
info->flags = FBINFO_FLAG_DEFAULT |
FBINFO_READS_FAST;
-
- fbi->max_xres = pdata->xres;
- info->var.xres = pdata->xres;
- info->var.xres_virtual = pdata->xres;
- fbi->max_yres = pdata->yres;
- info->var.yres = pdata->yres;
- info->var.yres_virtual = pdata->yres;
- fbi->max_bpp = pdata->bpp;
- info->var.bits_per_pixel = pdata->bpp;
- info->var.nonstd = pdata->nonstd;
- info->var.pixclock = pdata->pixclock;
- info->var.hsync_len = pdata->hsync_len;
- info->var.left_margin = pdata->left_margin;
- info->var.right_margin = pdata->right_margin;
- info->var.vsync_len = pdata->vsync_len;
- info->var.upper_margin = pdata->upper_margin;
- info->var.lower_margin = pdata->lower_margin;
- info->var.sync = pdata->sync;
info->var.grayscale = pdata->cmap_greyscale;
fbi->cmap_inverse = pdata->cmap_inverse;
fbi->cmap_static = pdata->cmap_static;
- fbi->pcr = pdata->pcr;
fbi->lscr1 = pdata->lscr1;
fbi->dmacr = pdata->dmacr;
fbi->pwmr = pdata->pwmr;
fbi->lcd_power = pdata->lcd_power;
fbi->backlight_power = pdata->backlight_power;
- info->fix.smem_len = fbi->max_xres * fbi->max_yres *
- fbi->max_bpp / 8;
+
+ for (i = 0, m = &pdata->mode[0]; i < pdata->num_modes; i++, m++)
+ info->fix.smem_len = max_t(size_t, info->fix.smem_len,
+ m->mode.xres * m->mode.yres * m->bpp / 8);
return 0;
}
@@ -642,9 +672,9 @@ static int __init imxfb_probe(struct platform_device *pdev)
struct fb_info *info;
struct imx_fb_platform_data *pdata;
struct resource *res;
- int ret;
+ int ret, i;
- printk("i.MX Framebuffer driver\n");
+ dev_info(&pdev->dev, "i.MX Framebuffer driver\n");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
@@ -662,6 +692,9 @@ static int __init imxfb_probe(struct platform_device *pdev)
fbi = info->par;
+ if (!fb_mode)
+ fb_mode = pdata->mode[0].mode.name;
+
platform_set_drvdata(pdev, info);
ret = imxfb_init_fbinfo(pdev);
@@ -684,7 +717,7 @@ static int __init imxfb_probe(struct platform_device *pdev)
fbi->regs = ioremap(res->start, resource_size(res));
if (fbi->regs == NULL) {
- printk(KERN_ERR"Cannot map frame buffer registers\n");
+ dev_err(&pdev->dev, "Cannot map frame buffer registers\n");
goto failed_ioremap;
}
@@ -719,6 +752,13 @@ static int __init imxfb_probe(struct platform_device *pdev)
goto failed_platform_init;
}
+ fbi->mode = pdata->mode;
+ fbi->num_modes = pdata->num_modes;
+
+ INIT_LIST_HEAD(&info->modelist);
+ for (i = 0; i < pdata->num_modes; i++)
+ fb_add_videomode(&pdata->mode[i].mode, &info->modelist);
+
/*
* This makes sure that our colour bitfield
* descriptors are correctly initialised.
@@ -754,7 +794,7 @@ failed_map:
failed_getclock:
iounmap(fbi->regs);
failed_ioremap:
- release_mem_region(res->start, res->end - res->start);
+ release_mem_region(res->start, resource_size(res));
failed_req:
kfree(info->pseudo_palette);
failed_init:
@@ -785,7 +825,7 @@ static int __devexit imxfb_remove(struct platform_device *pdev)
framebuffer_release(info);
iounmap(fbi->regs);
- release_mem_region(res->start, res->end - res->start + 1);
+ release_mem_region(res->start, resource_size(res));
clk_disable(fbi->clk);
clk_put(fbi->clk);
@@ -811,8 +851,34 @@ static struct platform_driver imxfb_driver = {
},
};
+static int imxfb_setup(void)
+{
+#ifndef MODULE
+ char *opt, *options = NULL;
+
+ if (fb_get_options("imxfb", &options))
+ return -ENODEV;
+
+ if (!options || !*options)
+ return 0;
+
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+ else
+ fb_mode = opt;
+ }
+#endif
+ return 0;
+}
+
int __init imxfb_init(void)
{
+ int ret = imxfb_setup();
+
+ if (ret < 0)
+ return ret;
+
return platform_driver_probe(&imxfb_driver, imxfb_probe);
}
diff --git a/fs/Kconfig b/fs/Kconfig
index 0e7da7bb5d9..455aa207e67 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -43,6 +43,7 @@ source "fs/xfs/Kconfig"
source "fs/gfs2/Kconfig"
source "fs/ocfs2/Kconfig"
source "fs/btrfs/Kconfig"
+source "fs/nilfs2/Kconfig"
endif # BLOCK
@@ -186,7 +187,6 @@ source "fs/romfs/Kconfig"
source "fs/sysv/Kconfig"
source "fs/ufs/Kconfig"
source "fs/exofs/Kconfig"
-source "fs/nilfs2/Kconfig"
endif # MISC_FILESYSTEMS
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 94dfda24c06..3581a4e5394 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1405,6 +1405,33 @@ static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg)
}
/*
+ * Write data to the block device. Only intended for the block device itself
+ * and the raw driver which basically is a fake block device.
+ *
+ * Does not take i_mutex for the write and thus is not for general purpose
+ * use.
+ */
+ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+{
+ struct file *file = iocb->ki_filp;
+ ssize_t ret;
+
+ BUG_ON(iocb->ki_pos != pos);
+
+ ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
+ if (ret > 0 || ret == -EIOCBQUEUED) {
+ ssize_t err;
+
+ err = generic_write_sync(file, pos, ret);
+ if (err < 0 && ret > 0)
+ ret = err;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(blkdev_aio_write);
+
+/*
* Try to release a page associated with block device when the system
* is under memory pressure.
*/
@@ -1436,7 +1463,7 @@ const struct file_operations def_blk_fops = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = generic_file_aio_read,
- .aio_write = generic_file_aio_write_nolock,
+ .aio_write = blkdev_aio_write,
.mmap = generic_file_mmap,
.fsync = block_fsync,
.unlocked_ioctl = block_ioctl,
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 72a2b9c28e9..535f85ba104 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1511,7 +1511,8 @@ static int remove_extent_backref(struct btrfs_trans_handle *trans,
static void btrfs_issue_discard(struct block_device *bdev,
u64 start, u64 len)
{
- blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL);
+ blkdev_issue_discard(bdev, start >> 9, len >> 9, GFP_KERNEL,
+ DISCARD_FL_BARRIER);
}
#endif
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 5dbefd11b4a..5cf405b0828 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -260,7 +260,7 @@ loop_lock:
num_run++;
batch_run++;
- if (bio_sync(cur))
+ if (bio_rw_flagged(cur, BIO_RW_SYNCIO))
num_sync_run++;
if (need_resched()) {
@@ -2903,7 +2903,7 @@ static noinline int schedule_bio(struct btrfs_root *root,
bio->bi_rw |= rw;
spin_lock(&device->io_lock);
- if (bio_sync(bio))
+ if (bio_rw_flagged(bio, BIO_RW_SYNCIO))
pending_bios = &device->pending_sync_bios;
else
pending_bios = &device->pending_bios;
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index e85b1e4389e..145540a316a 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -3,7 +3,10 @@ Version 1.60
Fix memory leak in reconnect. Fix oops in DFS mount error path.
Set s_maxbytes to smaller (the max that vfs can handle) so that
sendfile will now work over cifs mounts again. Add noforcegid
-and noforceuid mount parameters.
+and noforceuid mount parameters. Fix small mem leak when using
+ntlmv2. Fix 2nd mount to same server but with different port to
+be allowed (rather than reusing the 1st port) - only when the
+user explicitly overrides the port on the 2nd mount.
Version 1.59
------------
diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
index 051caecf7d6..8ec7736ce95 100644
--- a/fs/cifs/cifs_spnego.c
+++ b/fs/cifs/cifs_spnego.c
@@ -125,7 +125,7 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
if (server->addr.sockAddr.sin_family == AF_INET)
sprintf(dp, "ip4=%pI4", &server->addr.sockAddr.sin_addr);
else if (server->addr.sockAddr.sin_family == AF_INET6)
- sprintf(dp, "ip6=%pi6", &server->addr.sockAddr6.sin6_addr);
+ sprintf(dp, "ip6=%pI6", &server->addr.sockAddr6.sin6_addr);
else
goto out;
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index 6941c22398a..7dfe0842a6f 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -607,7 +607,7 @@ static struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
return get_cifs_acl_by_path(cifs_sb, path, pacllen);
pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->netfid, pacllen);
- atomic_dec(&open_file->wrtPending);
+ cifsFileInfo_put(open_file);
return pntsd;
}
@@ -665,7 +665,7 @@ static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen);
rc = set_cifs_acl_by_fid(cifs_sb, open_file->netfid, pnntsd, acllen);
- atomic_dec(&open_file->wrtPending);
+ cifsFileInfo_put(open_file);
return rc;
}
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index 7c9809523f4..7efe1745494 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -373,6 +373,7 @@ calc_exit_2:
compare with the NTLM example */
hmac_md5_final(ses->server->ntlmv2_hash, pctxt);
+ kfree(pctxt);
return rc;
}
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 84b75253b05..3610e9958b4 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -361,13 +361,10 @@ cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server)
static int
cifs_show_options(struct seq_file *s, struct vfsmount *m)
{
- struct cifs_sb_info *cifs_sb;
- struct cifsTconInfo *tcon;
-
- cifs_sb = CIFS_SB(m->mnt_sb);
- tcon = cifs_sb->tcon;
+ struct cifs_sb_info *cifs_sb = CIFS_SB(m->mnt_sb);
+ struct cifsTconInfo *tcon = cifs_sb->tcon;
- seq_printf(s, ",unc=%s", cifs_sb->tcon->treeName);
+ seq_printf(s, ",unc=%s", tcon->treeName);
if (tcon->ses->userName)
seq_printf(s, ",username=%s", tcon->ses->userName);
if (tcon->ses->domainName)
@@ -989,19 +986,19 @@ static int cifs_oplock_thread(void *dummyarg)
if (try_to_freeze())
continue;
- spin_lock(&GlobalMid_Lock);
- if (list_empty(&GlobalOplock_Q)) {
- spin_unlock(&GlobalMid_Lock);
+ spin_lock(&cifs_oplock_lock);
+ if (list_empty(&cifs_oplock_list)) {
+ spin_unlock(&cifs_oplock_lock);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(39*HZ);
} else {
- oplock_item = list_entry(GlobalOplock_Q.next,
+ oplock_item = list_entry(cifs_oplock_list.next,
struct oplock_q_entry, qhead);
cFYI(1, ("found oplock item to write out"));
pTcon = oplock_item->tcon;
inode = oplock_item->pinode;
netfid = oplock_item->netfid;
- spin_unlock(&GlobalMid_Lock);
+ spin_unlock(&cifs_oplock_lock);
DeleteOplockQEntry(oplock_item);
/* can not grab inode sem here since it would
deadlock when oplock received on delete
@@ -1058,7 +1055,7 @@ init_cifs(void)
int rc = 0;
cifs_proc_init();
INIT_LIST_HEAD(&cifs_tcp_ses_list);
- INIT_LIST_HEAD(&GlobalOplock_Q);
+ INIT_LIST_HEAD(&cifs_oplock_list);
#ifdef CONFIG_CIFS_EXPERIMENTAL
INIT_LIST_HEAD(&GlobalDnotifyReqList);
INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
@@ -1087,6 +1084,7 @@ init_cifs(void)
rwlock_init(&GlobalSMBSeslock);
rwlock_init(&cifs_tcp_ses_lock);
spin_lock_init(&GlobalMid_Lock);
+ spin_lock_init(&cifs_oplock_lock);
if (cifs_max_pending < 2) {
cifs_max_pending = 2;
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index 6c170948300..094325e3f71 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -113,5 +113,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern const struct export_operations cifs_export_ops;
#endif /* EXPERIMENTAL */
-#define CIFS_VERSION "1.60"
+#define CIFS_VERSION "1.61"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 6084d6379c0..6cfc81a3270 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -351,11 +351,24 @@ struct cifsFileInfo {
bool closePend:1; /* file is marked to close */
bool invalidHandle:1; /* file closed via session abend */
bool messageMode:1; /* for pipes: message vs byte mode */
- atomic_t wrtPending; /* handle in use - defer close */
+ atomic_t count; /* reference count */
struct mutex fh_mutex; /* prevents reopen race after dead ses*/
struct cifs_search_info srch_inf;
};
+/* Take a reference on the file private data */
+static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file)
+{
+ atomic_inc(&cifs_file->count);
+}
+
+/* Release a reference on the file private data */
+static inline void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
+{
+ if (atomic_dec_and_test(&cifs_file->count))
+ kfree(cifs_file);
+}
+
/*
* One of these for each file inode
*/
@@ -656,7 +669,11 @@ GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock;
*/
GLOBAL_EXTERN rwlock_t GlobalSMBSeslock;
-GLOBAL_EXTERN struct list_head GlobalOplock_Q;
+/* Global list of oplocks */
+GLOBAL_EXTERN struct list_head cifs_oplock_list;
+
+/* Protects the cifs_oplock_list */
+GLOBAL_EXTERN spinlock_t cifs_oplock_lock;
/* Outstanding dir notify requests */
GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 1866bc2927d..301e307e127 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -100,110 +100,138 @@ static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
to this tcon */
}
-/* Allocate and return pointer to an SMB request buffer, and set basic
- SMB information in the SMB header. If the return code is zero, this
- function must have filled in request_buf pointer */
+/* reconnect the socket, tcon, and smb session if needed */
static int
-small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
- void **request_buf)
+cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command)
{
int rc = 0;
+ struct cifsSesInfo *ses;
+ struct TCP_Server_Info *server;
+ struct nls_table *nls_codepage;
- /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
- check for tcp and smb session status done differently
- for those three - in the calling routine */
- if (tcon) {
- if (tcon->tidStatus == CifsExiting) {
- /* only tree disconnect, open, and write,
- (and ulogoff which does not have tcon)
- are allowed as we start force umount */
- if ((smb_command != SMB_COM_WRITE_ANDX) &&
- (smb_command != SMB_COM_OPEN_ANDX) &&
- (smb_command != SMB_COM_TREE_DISCONNECT)) {
- cFYI(1, ("can not send cmd %d while umounting",
- smb_command));
- return -ENODEV;
- }
+ /*
+ * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
+ * tcp and smb session status done differently for those three - in the
+ * calling routine
+ */
+ if (!tcon)
+ return 0;
+
+ ses = tcon->ses;
+ server = ses->server;
+
+ /*
+ * only tree disconnect, open, and write, (and ulogoff which does not
+ * have tcon) are allowed as we start force umount
+ */
+ if (tcon->tidStatus == CifsExiting) {
+ if (smb_command != SMB_COM_WRITE_ANDX &&
+ smb_command != SMB_COM_OPEN_ANDX &&
+ smb_command != SMB_COM_TREE_DISCONNECT) {
+ cFYI(1, ("can not send cmd %d while umounting",
+ smb_command));
+ return -ENODEV;
}
- if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
- (tcon->ses->server)) {
- struct nls_table *nls_codepage;
- /* Give Demultiplex thread up to 10 seconds to
- reconnect, should be greater than cifs socket
- timeout which is 7 seconds */
- while (tcon->ses->server->tcpStatus ==
- CifsNeedReconnect) {
- wait_event_interruptible_timeout(tcon->ses->server->response_q,
- (tcon->ses->server->tcpStatus ==
- CifsGood), 10 * HZ);
- if (tcon->ses->server->tcpStatus ==
- CifsNeedReconnect) {
- /* on "soft" mounts we wait once */
- if (!tcon->retry ||
- (tcon->ses->status == CifsExiting)) {
- cFYI(1, ("gave up waiting on "
- "reconnect in smb_init"));
- return -EHOSTDOWN;
- } /* else "hard" mount - keep retrying
- until process is killed or server
- comes back on-line */
- } else /* TCP session is reestablished now */
- break;
- }
+ }
- nls_codepage = load_nls_default();
- /* need to prevent multiple threads trying to
- simultaneously reconnect the same SMB session */
- down(&tcon->ses->sesSem);
- if (tcon->ses->need_reconnect)
- rc = cifs_setup_session(0, tcon->ses,
- nls_codepage);
- if (!rc && (tcon->need_reconnect)) {
- mark_open_files_invalid(tcon);
- rc = CIFSTCon(0, tcon->ses, tcon->treeName,
- tcon, nls_codepage);
- up(&tcon->ses->sesSem);
- /* BB FIXME add code to check if wsize needs
- update due to negotiated smb buffer size
- shrinking */
- if (rc == 0) {
- atomic_inc(&tconInfoReconnectCount);
- /* tell server Unix caps we support */
- if (tcon->ses->capabilities & CAP_UNIX)
- reset_cifs_unix_caps(
- 0 /* no xid */,
- tcon,
- NULL /* we do not know sb */,
- NULL /* no vol info */);
- }
+ if (ses->status == CifsExiting)
+ return -EIO;
- cFYI(1, ("reconnect tcon rc = %d", rc));
- /* Removed call to reopen open files here.
- It is safer (and faster) to reopen files
- one at a time as needed in read and write */
-
- /* Check if handle based operation so we
- know whether we can continue or not without
- returning to caller to reset file handle */
- switch (smb_command) {
- case SMB_COM_READ_ANDX:
- case SMB_COM_WRITE_ANDX:
- case SMB_COM_CLOSE:
- case SMB_COM_FIND_CLOSE2:
- case SMB_COM_LOCKING_ANDX: {
- unload_nls(nls_codepage);
- return -EAGAIN;
- }
- }
- } else {
- up(&tcon->ses->sesSem);
- }
- unload_nls(nls_codepage);
+ /*
+ * Give demultiplex thread up to 10 seconds to reconnect, should be
+ * greater than cifs socket timeout which is 7 seconds
+ */
+ while (server->tcpStatus == CifsNeedReconnect) {
+ wait_event_interruptible_timeout(server->response_q,
+ (server->tcpStatus == CifsGood), 10 * HZ);
- } else {
- return -EIO;
+ /* is TCP session is reestablished now ?*/
+ if (server->tcpStatus != CifsNeedReconnect)
+ break;
+
+ /*
+ * on "soft" mounts we wait once. Hard mounts keep
+ * retrying until process is killed or server comes
+ * back on-line
+ */
+ if (!tcon->retry || ses->status == CifsExiting) {
+ cFYI(1, ("gave up waiting on reconnect in smb_init"));
+ return -EHOSTDOWN;
}
}
+
+ if (!ses->need_reconnect && !tcon->need_reconnect)
+ return 0;
+
+ nls_codepage = load_nls_default();
+
+ /*
+ * need to prevent multiple threads trying to simultaneously
+ * reconnect the same SMB session
+ */
+ down(&ses->sesSem);
+ if (ses->need_reconnect)
+ rc = cifs_setup_session(0, ses, nls_codepage);
+
+ /* do we need to reconnect tcon? */
+ if (rc || !tcon->need_reconnect) {
+ up(&ses->sesSem);
+ goto out;
+ }
+
+ mark_open_files_invalid(tcon);
+ rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage);
+ up(&ses->sesSem);
+ cFYI(1, ("reconnect tcon rc = %d", rc));
+
+ if (rc)
+ goto out;
+
+ /*
+ * FIXME: check if wsize needs updated due to negotiated smb buffer
+ * size shrinking
+ */
+ atomic_inc(&tconInfoReconnectCount);
+
+ /* tell server Unix caps we support */
+ if (ses->capabilities & CAP_UNIX)
+ reset_cifs_unix_caps(0, tcon, NULL, NULL);
+
+ /*
+ * Removed call to reopen open files here. It is safer (and faster) to
+ * reopen files one at a time as needed in read and write.
+ *
+ * FIXME: what about file locks? don't we need to reclaim them ASAP?
+ */
+
+out:
+ /*
+ * Check if handle based operation so we know whether we can continue
+ * or not without returning to caller to reset file handle
+ */
+ switch (smb_command) {
+ case SMB_COM_READ_ANDX:
+ case SMB_COM_WRITE_ANDX:
+ case SMB_COM_CLOSE:
+ case SMB_COM_FIND_CLOSE2:
+ case SMB_COM_LOCKING_ANDX:
+ rc = -EAGAIN;
+ }
+
+ unload_nls(nls_codepage);
+ return rc;
+}
+
+/* Allocate and return pointer to an SMB request buffer, and set basic
+ SMB information in the SMB header. If the return code is zero, this
+ function must have filled in request_buf pointer */
+static int
+small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
+ void **request_buf)
+{
+ int rc = 0;
+
+ rc = cifs_reconnect_tcon(tcon, smb_command);
if (rc)
return rc;
@@ -256,101 +284,7 @@ smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
{
int rc = 0;
- /* SMBs NegProt, SessSetup, uLogoff do not have tcon yet so
- check for tcp and smb session status done differently
- for those three - in the calling routine */
- if (tcon) {
- if (tcon->tidStatus == CifsExiting) {
- /* only tree disconnect, open, and write,
- (and ulogoff which does not have tcon)
- are allowed as we start force umount */
- if ((smb_command != SMB_COM_WRITE_ANDX) &&
- (smb_command != SMB_COM_OPEN_ANDX) &&
- (smb_command != SMB_COM_TREE_DISCONNECT)) {
- cFYI(1, ("can not send cmd %d while umounting",
- smb_command));
- return -ENODEV;
- }
- }
-
- if ((tcon->ses) && (tcon->ses->status != CifsExiting) &&
- (tcon->ses->server)) {
- struct nls_table *nls_codepage;
- /* Give Demultiplex thread up to 10 seconds to
- reconnect, should be greater than cifs socket
- timeout which is 7 seconds */
- while (tcon->ses->server->tcpStatus ==
- CifsNeedReconnect) {
- wait_event_interruptible_timeout(tcon->ses->server->response_q,
- (tcon->ses->server->tcpStatus ==
- CifsGood), 10 * HZ);
- if (tcon->ses->server->tcpStatus ==
- CifsNeedReconnect) {
- /* on "soft" mounts we wait once */
- if (!tcon->retry ||
- (tcon->ses->status == CifsExiting)) {
- cFYI(1, ("gave up waiting on "
- "reconnect in smb_init"));
- return -EHOSTDOWN;
- } /* else "hard" mount - keep retrying
- until process is killed or server
- comes on-line */
- } else /* TCP session is reestablished now */
- break;
- }
- nls_codepage = load_nls_default();
- /* need to prevent multiple threads trying to
- simultaneously reconnect the same SMB session */
- down(&tcon->ses->sesSem);
- if (tcon->ses->need_reconnect)
- rc = cifs_setup_session(0, tcon->ses,
- nls_codepage);
- if (!rc && (tcon->need_reconnect)) {
- mark_open_files_invalid(tcon);
- rc = CIFSTCon(0, tcon->ses, tcon->treeName,
- tcon, nls_codepage);
- up(&tcon->ses->sesSem);
- /* BB FIXME add code to check if wsize needs
- update due to negotiated smb buffer size
- shrinking */
- if (rc == 0) {
- atomic_inc(&tconInfoReconnectCount);
- /* tell server Unix caps we support */
- if (tcon->ses->capabilities & CAP_UNIX)
- reset_cifs_unix_caps(
- 0 /* no xid */,
- tcon,
- NULL /* do not know sb */,
- NULL /* no vol info */);
- }
-
- cFYI(1, ("reconnect tcon rc = %d", rc));
- /* Removed call to reopen open files here.
- It is safer (and faster) to reopen files
- one at a time as needed in read and write */
-
- /* Check if handle based operation so we
- know whether we can continue or not without
- returning to caller to reset file handle */
- switch (smb_command) {
- case SMB_COM_READ_ANDX:
- case SMB_COM_WRITE_ANDX:
- case SMB_COM_CLOSE:
- case SMB_COM_FIND_CLOSE2:
- case SMB_COM_LOCKING_ANDX: {
- unload_nls(nls_codepage);
- return -EAGAIN;
- }
- }
- } else {
- up(&tcon->ses->sesSem);
- }
- unload_nls(nls_codepage);
-
- } else {
- return -EIO;
- }
- }
+ rc = cifs_reconnect_tcon(tcon, smb_command);
if (rc)
return rc;
@@ -3961,6 +3895,10 @@ parse_DFS_referrals(TRANSACTION2_GET_DFS_REFER_RSP *pSMBr,
if (is_unicode) {
__le16 *tmp = kmalloc(strlen(searchName)*2 + 2,
GFP_KERNEL);
+ if (tmp == NULL) {
+ rc = -ENOMEM;
+ goto parse_DFS_referrals_exit;
+ }
cifsConvertToUCS((__le16 *) tmp, searchName,
PATH_MAX, nls_codepage, remap);
node->path_consumed = cifs_ucs2_bytes(tmp,
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 1f3345d7fa7..d49682433c2 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -1377,7 +1377,7 @@ cifs_parse_mount_options(char *options, const char *devname,
}
static struct TCP_Server_Info *
-cifs_find_tcp_session(struct sockaddr_storage *addr)
+cifs_find_tcp_session(struct sockaddr_storage *addr, unsigned short int port)
{
struct list_head *tmp;
struct TCP_Server_Info *server;
@@ -1397,16 +1397,37 @@ cifs_find_tcp_session(struct sockaddr_storage *addr)
if (server->tcpStatus == CifsNew)
continue;
- if (addr->ss_family == AF_INET &&
- (addr4->sin_addr.s_addr !=
- server->addr.sockAddr.sin_addr.s_addr))
- continue;
- else if (addr->ss_family == AF_INET6 &&
- (!ipv6_addr_equal(&server->addr.sockAddr6.sin6_addr,
- &addr6->sin6_addr) ||
- server->addr.sockAddr6.sin6_scope_id !=
- addr6->sin6_scope_id))
- continue;
+ switch (addr->ss_family) {
+ case AF_INET:
+ if (addr4->sin_addr.s_addr ==
+ server->addr.sockAddr.sin_addr.s_addr) {
+ addr4->sin_port = htons(port);
+ /* user overrode default port? */
+ if (addr4->sin_port) {
+ if (addr4->sin_port !=
+ server->addr.sockAddr.sin_port)
+ continue;
+ }
+ break;
+ } else
+ continue;
+
+ case AF_INET6:
+ if (ipv6_addr_equal(&addr6->sin6_addr,
+ &server->addr.sockAddr6.sin6_addr) &&
+ (addr6->sin6_scope_id ==
+ server->addr.sockAddr6.sin6_scope_id)) {
+ addr6->sin6_port = htons(port);
+ /* user overrode default port? */
+ if (addr6->sin6_port) {
+ if (addr6->sin6_port !=
+ server->addr.sockAddr6.sin6_port)
+ continue;
+ }
+ break;
+ } else
+ continue;
+ }
++server->srv_count;
write_unlock(&cifs_tcp_ses_lock);
@@ -1475,7 +1496,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
}
/* see if we already have a matching tcp_ses */
- tcp_ses = cifs_find_tcp_session(&addr);
+ tcp_ses = cifs_find_tcp_session(&addr, volume_info->port);
if (tcp_ses)
return tcp_ses;
@@ -2636,9 +2657,9 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
return -EIO;
smb_buffer = cifs_buf_get();
- if (smb_buffer == NULL) {
+ if (smb_buffer == NULL)
return -ENOMEM;
- }
+
smb_buffer_response = smb_buffer;
header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 4326ffd90fa..a6424cfc012 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -153,7 +153,7 @@ cifs_fill_fileinfo(struct inode *newinode, __u16 fileHandle,
mutex_init(&pCifsFile->fh_mutex);
mutex_init(&pCifsFile->lock_mutex);
INIT_LIST_HEAD(&pCifsFile->llist);
- atomic_set(&pCifsFile->wrtPending, 0);
+ atomic_set(&pCifsFile->count, 1);
/* set the following in open now
pCifsFile->pfile = file; */
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index c34b7f8a217..fa7beac8b80 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -53,11 +53,9 @@ static inline struct cifsFileInfo *cifs_init_private(
private_data->pInode = inode;
private_data->invalidHandle = false;
private_data->closePend = false;
- /* we have to track num writers to the inode, since writepages
- does not tell us which handle the write is for so there can
- be a close (overlapping with write) of the filehandle that
- cifs_writepages chose to use */
- atomic_set(&private_data->wrtPending, 0);
+ /* Initialize reference count to one. The private data is
+ freed on the release of the last reference */
+ atomic_set(&private_data->count, 1);
return private_data;
}
@@ -643,7 +641,7 @@ int cifs_close(struct inode *inode, struct file *file)
if (!pTcon->need_reconnect) {
write_unlock(&GlobalSMBSeslock);
timeout = 2;
- while ((atomic_read(&pSMBFile->wrtPending) != 0)
+ while ((atomic_read(&pSMBFile->count) != 1)
&& (timeout <= 2048)) {
/* Give write a better chance to get to
server ahead of the close. We do not
@@ -657,8 +655,6 @@ int cifs_close(struct inode *inode, struct file *file)
msleep(timeout);
timeout *= 4;
}
- if (atomic_read(&pSMBFile->wrtPending))
- cERROR(1, ("close with pending write"));
if (!pTcon->need_reconnect &&
!pSMBFile->invalidHandle)
rc = CIFSSMBClose(xid, pTcon,
@@ -681,24 +677,7 @@ int cifs_close(struct inode *inode, struct file *file)
list_del(&pSMBFile->flist);
list_del(&pSMBFile->tlist);
write_unlock(&GlobalSMBSeslock);
- timeout = 10;
- /* We waited above to give the SMBWrite a chance to issue
- on the wire (so we do not get SMBWrite returning EBADF
- if writepages is racing with close. Note that writepages
- does not specify a file handle, so it is possible for a file
- to be opened twice, and the application close the "wrong"
- file handle - in these cases we delay long enough to allow
- the SMBWrite to get on the wire before the SMB Close.
- We allow total wait here over 45 seconds, more than
- oplock break time, and more than enough to allow any write
- to complete on the server, or to time out on the client */
- while ((atomic_read(&pSMBFile->wrtPending) != 0)
- && (timeout <= 50000)) {
- cERROR(1, ("writes pending, delay free of handle"));
- msleep(timeout);
- timeout *= 8;
- }
- kfree(file->private_data);
+ cifsFileInfo_put(file->private_data);
file->private_data = NULL;
} else
rc = -EBADF;
@@ -1236,7 +1215,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
if (!open_file->invalidHandle) {
/* found a good file */
/* lock it so it will not be closed on us */
- atomic_inc(&open_file->wrtPending);
+ cifsFileInfo_get(open_file);
read_unlock(&GlobalSMBSeslock);
return open_file;
} /* else might as well continue, and look for
@@ -1276,7 +1255,7 @@ refind_writable:
if (open_file->pfile &&
((open_file->pfile->f_flags & O_RDWR) ||
(open_file->pfile->f_flags & O_WRONLY))) {
- atomic_inc(&open_file->wrtPending);
+ cifsFileInfo_get(open_file);
if (!open_file->invalidHandle) {
/* found a good writable file */
@@ -1293,7 +1272,7 @@ refind_writable:
else { /* start over in case this was deleted */
/* since the list could be modified */
read_lock(&GlobalSMBSeslock);
- atomic_dec(&open_file->wrtPending);
+ cifsFileInfo_put(open_file);
goto refind_writable;
}
}
@@ -1309,7 +1288,7 @@ refind_writable:
read_lock(&GlobalSMBSeslock);
/* can not use this handle, no write
pending on this one after all */
- atomic_dec(&open_file->wrtPending);
+ cifsFileInfo_put(open_file);
if (open_file->closePend) /* list could have changed */
goto refind_writable;
@@ -1373,7 +1352,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
if (open_file) {
bytes_written = cifs_write(open_file->pfile, write_data,
to-from, &offset);
- atomic_dec(&open_file->wrtPending);
+ cifsFileInfo_put(open_file);
/* Does mm or vfs already set times? */
inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb);
if ((bytes_written > 0) && (offset))
@@ -1562,7 +1541,7 @@ retry:
bytes_to_write, offset,
&bytes_written, iov, n_iov,
long_op);
- atomic_dec(&open_file->wrtPending);
+ cifsFileInfo_put(open_file);
cifs_update_eof(cifsi, offset, bytes_written);
if (rc || bytes_written < bytes_to_write) {
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 82d83839655..1f09c761931 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -800,7 +800,7 @@ set_via_filehandle:
if (open_file == NULL)
CIFSSMBClose(xid, pTcon, netfid);
else
- atomic_dec(&open_file->wrtPending);
+ cifsFileInfo_put(open_file);
out:
return rc;
}
@@ -1635,7 +1635,7 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
__u32 npid = open_file->pid;
rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid,
npid, false);
- atomic_dec(&open_file->wrtPending);
+ cifsFileInfo_put(open_file);
cFYI(1, ("SetFSize for attrs rc = %d", rc));
if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
unsigned int bytes_written;
@@ -1790,7 +1790,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
u16 nfid = open_file->netfid;
u32 npid = open_file->pid;
rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid);
- atomic_dec(&open_file->wrtPending);
+ cifsFileInfo_put(open_file);
} else {
rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args,
cifs_sb->local_nls,
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 0ad3e2d116a..1da4ab250ea 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -119,20 +119,19 @@ AllocOplockQEntry(struct inode *pinode, __u16 fid, struct cifsTconInfo *tcon)
temp->pinode = pinode;
temp->tcon = tcon;
temp->netfid = fid;
- spin_lock(&GlobalMid_Lock);
- list_add_tail(&temp->qhead, &GlobalOplock_Q);
- spin_unlock(&GlobalMid_Lock);
+ spin_lock(&cifs_oplock_lock);
+ list_add_tail(&temp->qhead, &cifs_oplock_list);
+ spin_unlock(&cifs_oplock_lock);
}
return temp;
-
}
void DeleteOplockQEntry(struct oplock_q_entry *oplockEntry)
{
- spin_lock(&GlobalMid_Lock);
+ spin_lock(&cifs_oplock_lock);
/* should we check if list empty first? */
list_del(&oplockEntry->qhead);
- spin_unlock(&GlobalMid_Lock);
+ spin_unlock(&cifs_oplock_lock);
kmem_cache_free(cifs_oplock_cachep, oplockEntry);
}
@@ -144,14 +143,14 @@ void DeleteTconOplockQEntries(struct cifsTconInfo *tcon)
if (tcon == NULL)
return;
- spin_lock(&GlobalMid_Lock);
- list_for_each_entry(temp, &GlobalOplock_Q, qhead) {
+ spin_lock(&cifs_oplock_lock);
+ list_for_each_entry(temp, &cifs_oplock_list, qhead) {
if ((temp->tcon) && (temp->tcon == tcon)) {
list_del(&temp->qhead);
kmem_cache_free(cifs_oplock_cachep, temp);
}
}
- spin_unlock(&GlobalMid_Lock);
+ spin_unlock(&cifs_oplock_lock);
}
static int
diff --git a/fs/ext2/inode.c b/fs/ext2/inode.c
index e27130341d4..1c1638f873a 100644
--- a/fs/ext2/inode.c
+++ b/fs/ext2/inode.c
@@ -482,7 +482,7 @@ static int ext2_alloc_branch(struct inode *inode,
unlock_buffer(bh);
mark_buffer_dirty_inode(bh, inode);
/* We used to sync bh here if IS_SYNC(inode).
- * But we now rely upon generic_osync_inode()
+ * But we now rely upon generic_write_sync()
* and b_inode_buffers. But not for directories.
*/
if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode))
diff --git a/fs/ext3/file.c b/fs/ext3/file.c
index 29925321478..388bbdfa0b4 100644
--- a/fs/ext3/file.c
+++ b/fs/ext3/file.c
@@ -51,71 +51,12 @@ static int ext3_release_file (struct inode * inode, struct file * filp)
return 0;
}
-static ssize_t
-ext3_file_write(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
-{
- struct file *file = iocb->ki_filp;
- struct inode *inode = file->f_path.dentry->d_inode;
- ssize_t ret;
- int err;
-
- ret = generic_file_aio_write(iocb, iov, nr_segs, pos);
-
- /*
- * Skip flushing if there was an error, or if nothing was written.
- */
- if (ret <= 0)
- return ret;
-
- /*
- * If the inode is IS_SYNC, or is O_SYNC and we are doing data
- * journalling then we need to make sure that we force the transaction
- * to disk to keep all metadata uptodate synchronously.
- */
- if (file->f_flags & O_SYNC) {
- /*
- * If we are non-data-journaled, then the dirty data has
- * already been flushed to backing store by generic_osync_inode,
- * and the inode has been flushed too if there have been any
- * modifications other than mere timestamp updates.
- *
- * Open question --- do we care about flushing timestamps too
- * if the inode is IS_SYNC?
- */
- if (!ext3_should_journal_data(inode))
- return ret;
-
- goto force_commit;
- }
-
- /*
- * So we know that there has been no forced data flush. If the inode
- * is marked IS_SYNC, we need to force one ourselves.
- */
- if (!IS_SYNC(inode))
- return ret;
-
- /*
- * Open question #2 --- should we force data to disk here too? If we
- * don't, the only impact is that data=writeback filesystems won't
- * flush data to disk automatically on IS_SYNC, only metadata (but
- * historically, that is what ext2 has done.)
- */
-
-force_commit:
- err = ext3_force_commit(inode->i_sb);
- if (err)
- return err;
- return ret;
-}
-
const struct file_operations ext3_file_operations = {
.llseek = generic_file_llseek,
.read = do_sync_read,
.write = do_sync_write,
.aio_read = generic_file_aio_read,
- .aio_write = ext3_file_write,
+ .aio_write = generic_file_aio_write,
.unlocked_ioctl = ext3_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ext3_compat_ioctl,
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index 27f3c5354c0..5ca3eca70a1 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -58,10 +58,7 @@ static ssize_t
ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
- struct file *file = iocb->ki_filp;
- struct inode *inode = file->f_path.dentry->d_inode;
- ssize_t ret;
- int err;
+ struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
/*
* If we have encountered a bitmap-format file, the size limit
@@ -81,53 +78,7 @@ ext4_file_write(struct kiocb *iocb, const struct iovec *iov,
}
}
- ret = generic_file_aio_write(iocb, iov, nr_segs, pos);
- /*
- * Skip flushing if there was an error, or if nothing was written.
- */
- if (ret <= 0)
- return ret;
-
- /*
- * If the inode is IS_SYNC, or is O_SYNC and we are doing data
- * journalling then we need to make sure that we force the transaction
- * to disk to keep all metadata uptodate synchronously.
- */
- if (file->f_flags & O_SYNC) {
- /*
- * If we are non-data-journaled, then the dirty data has
- * already been flushed to backing store by generic_osync_inode,
- * and the inode has been flushed too if there have been any
- * modifications other than mere timestamp updates.
- *
- * Open question --- do we care about flushing timestamps too
- * if the inode is IS_SYNC?
- */
- if (!ext4_should_journal_data(inode))
- return ret;
-
- goto force_commit;
- }
-
- /*
- * So we know that there has been no forced data flush. If the inode
- * is marked IS_SYNC, we need to force one ourselves.
- */
- if (!IS_SYNC(inode))
- return ret;
-
- /*
- * Open question #2 --- should we force data to disk here too? If we
- * don't, the only impact is that data=writeback filesystems won't
- * flush data to disk automatically on IS_SYNC, only metadata (but
- * historically, that is what ext2 has done.)
- */
-
-force_commit:
- err = ext4_force_commit(inode->i_sb);
- if (err)
- return err;
- return ret;
+ return generic_file_aio_write(iocb, iov, nr_segs, pos);
}
static struct vm_operations_struct ext4_file_vm_ops = {
diff --git a/fs/fat/file.c b/fs/fat/file.c
index f042b965c95..e8c159de236 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -176,8 +176,26 @@ static int fat_cont_expand(struct inode *inode, loff_t size)
inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
mark_inode_dirty(inode);
- if (IS_SYNC(inode))
- err = sync_page_range_nolock(inode, mapping, start, count);
+ if (IS_SYNC(inode)) {
+ int err2;
+
+ /*
+ * Opencode syncing since we don't have a file open to use
+ * standard fsync path.
+ */
+ err = filemap_fdatawrite_range(mapping, start,
+ start + count - 1);
+ err2 = sync_mapping_buffers(mapping);
+ if (!err)
+ err = err2;
+ err2 = write_inode_now(inode, 1);
+ if (!err)
+ err = err2;
+ if (!err) {
+ err = filemap_fdatawait_range(mapping, start,
+ start + count - 1);
+ }
+ }
out:
return err;
}
diff --git a/fs/fat/misc.c b/fs/fat/misc.c
index a6c20473dfd..4e35be873e0 100644
--- a/fs/fat/misc.c
+++ b/fs/fat/misc.c
@@ -119,8 +119,8 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster)
MSDOS_I(inode)->i_start = new_dclus;
MSDOS_I(inode)->i_logstart = new_dclus;
/*
- * Since generic_osync_inode() synchronize later if
- * this is not directory, we don't here.
+ * Since generic_write_sync() synchronizes regular files later,
+ * we sync here only directories.
*/
if (S_ISDIR(inode->i_mode) && IS_DIRSYNC(inode)) {
ret = fat_sync_inode(inode);
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index da86ef58e42..628235cf44b 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -1242,57 +1242,3 @@ int sync_inode(struct inode *inode, struct writeback_control *wbc)
return ret;
}
EXPORT_SYMBOL(sync_inode);
-
-/**
- * generic_osync_inode - flush all dirty data for a given inode to disk
- * @inode: inode to write
- * @mapping: the address_space that should be flushed
- * @what: what to write and wait upon
- *
- * This can be called by file_write functions for files which have the
- * O_SYNC flag set, to flush dirty writes to disk.
- *
- * @what is a bitmask, specifying which part of the inode's data should be
- * written and waited upon.
- *
- * OSYNC_DATA: i_mapping's dirty data
- * OSYNC_METADATA: the buffers at i_mapping->private_list
- * OSYNC_INODE: the inode itself
- */
-
-int generic_osync_inode(struct inode *inode, struct address_space *mapping, int what)
-{
- int err = 0;
- int need_write_inode_now = 0;
- int err2;
-
- if (what & OSYNC_DATA)
- err = filemap_fdatawrite(mapping);
- if (what & (OSYNC_METADATA|OSYNC_DATA)) {
- err2 = sync_mapping_buffers(mapping);
- if (!err)
- err = err2;
- }
- if (what & OSYNC_DATA) {
- err2 = filemap_fdatawait(mapping);
- if (!err)
- err = err2;
- }
-
- spin_lock(&inode_lock);
- if ((inode->i_state & I_DIRTY) &&
- ((what & OSYNC_INODE) || (inode->i_state & I_DIRTY_DATASYNC)))
- need_write_inode_now = 1;
- spin_unlock(&inode_lock);
-
- if (need_write_inode_now) {
- err2 = write_inode_now(inode, 1);
- if (!err)
- err = err2;
- }
- else
- inode_sync_wait(inode);
-
- return err;
-}
-EXPORT_SYMBOL(generic_osync_inode);
diff --git a/fs/gfs2/Makefile b/fs/gfs2/Makefile
index 3da2f1f4f73..21f7e46da4c 100644
--- a/fs/gfs2/Makefile
+++ b/fs/gfs2/Makefile
@@ -1,6 +1,6 @@
EXTRA_CFLAGS := -I$(src)
obj-$(CONFIG_GFS2_FS) += gfs2.o
-gfs2-y := acl.o bmap.o dir.o eaops.o eattr.o glock.o \
+gfs2-y := acl.o bmap.o dir.o xattr.o glock.o \
glops.o inode.o log.o lops.o main.o meta_io.o \
aops.o dentry.o export.o file.o \
ops_fstype.o ops_inode.o quota.o \
diff --git a/fs/gfs2/acl.c b/fs/gfs2/acl.c
index fa881bdc3d8..3fc4e3ac7d8 100644
--- a/fs/gfs2/acl.c
+++ b/fs/gfs2/acl.c
@@ -19,8 +19,7 @@
#include "gfs2.h"
#include "incore.h"
#include "acl.h"
-#include "eaops.h"
-#include "eattr.h"
+#include "xattr.h"
#include "glock.h"
#include "inode.h"
#include "meta_io.h"
@@ -31,8 +30,7 @@
#define ACL_DEFAULT 0
int gfs2_acl_validate_set(struct gfs2_inode *ip, int access,
- struct gfs2_ea_request *er,
- int *remove, mode_t *mode)
+ struct gfs2_ea_request *er, int *remove, mode_t *mode)
{
struct posix_acl *acl;
int error;
@@ -83,30 +81,20 @@ int gfs2_acl_validate_remove(struct gfs2_inode *ip, int access)
return 0;
}
-static int acl_get(struct gfs2_inode *ip, int access, struct posix_acl **acl,
- struct gfs2_ea_location *el, char **data, unsigned int *len)
+static int acl_get(struct gfs2_inode *ip, const char *name,
+ struct posix_acl **acl, struct gfs2_ea_location *el,
+ char **datap, unsigned int *lenp)
{
- struct gfs2_ea_request er;
- struct gfs2_ea_location el_this;
+ char *data;
+ unsigned int len;
int error;
+ el->el_bh = NULL;
+
if (!ip->i_eattr)
return 0;
- memset(&er, 0, sizeof(struct gfs2_ea_request));
- if (access) {
- er.er_name = GFS2_POSIX_ACL_ACCESS;
- er.er_name_len = GFS2_POSIX_ACL_ACCESS_LEN;
- } else {
- er.er_name = GFS2_POSIX_ACL_DEFAULT;
- er.er_name_len = GFS2_POSIX_ACL_DEFAULT_LEN;
- }
- er.er_type = GFS2_EATYPE_SYS;
-
- if (!el)
- el = &el_this;
-
- error = gfs2_ea_find(ip, &er, el);
+ error = gfs2_ea_find(ip, GFS2_EATYPE_SYS, name, el);
if (error)
return error;
if (!el->el_ea)
@@ -114,32 +102,31 @@ static int acl_get(struct gfs2_inode *ip, int access, struct posix_acl **acl,
if (!GFS2_EA_DATA_LEN(el->el_ea))
goto out;
- er.er_data_len = GFS2_EA_DATA_LEN(el->el_ea);
- er.er_data = kmalloc(er.er_data_len, GFP_NOFS);
+ len = GFS2_EA_DATA_LEN(el->el_ea);
+ data = kmalloc(len, GFP_NOFS);
error = -ENOMEM;
- if (!er.er_data)
+ if (!data)
goto out;
- error = gfs2_ea_get_copy(ip, el, er.er_data);
- if (error)
+ error = gfs2_ea_get_copy(ip, el, data, len);
+ if (error < 0)
goto out_kfree;
+ error = 0;
if (acl) {
- *acl = posix_acl_from_xattr(er.er_data, er.er_data_len);
+ *acl = posix_acl_from_xattr(data, len);
if (IS_ERR(*acl))
error = PTR_ERR(*acl);
}
out_kfree:
- if (error || !data)
- kfree(er.er_data);
- else {
- *data = er.er_data;
- *len = er.er_data_len;
+ if (error || !datap) {
+ kfree(data);
+ } else {
+ *datap = data;
+ *lenp = len;
}
out:
- if (error || el == &el_this)
- brelse(el->el_bh);
return error;
}
@@ -153,10 +140,12 @@ out:
int gfs2_check_acl(struct inode *inode, int mask)
{
+ struct gfs2_ea_location el;
struct posix_acl *acl = NULL;
int error;
- error = acl_get(GFS2_I(inode), ACL_ACCESS, &acl, NULL, NULL, NULL);
+ error = acl_get(GFS2_I(inode), GFS2_POSIX_ACL_ACCESS, &acl, &el, NULL, NULL);
+ brelse(el.el_bh);
if (error)
return error;
@@ -196,10 +185,12 @@ static int munge_mode(struct gfs2_inode *ip, mode_t mode)
int gfs2_acl_create(struct gfs2_inode *dip, struct gfs2_inode *ip)
{
+ struct gfs2_ea_location el;
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
struct posix_acl *acl = NULL, *clone;
- struct gfs2_ea_request er;
mode_t mode = ip->i_inode.i_mode;
+ char *data = NULL;
+ unsigned int len;
int error;
if (!sdp->sd_args.ar_posix_acl)
@@ -207,11 +198,8 @@ int gfs2_acl_create(struct gfs2_inode *dip, struct gfs2_inode *ip)
if (S_ISLNK(ip->i_inode.i_mode))
return 0;
- memset(&er, 0, sizeof(struct gfs2_ea_request));
- er.er_type = GFS2_EATYPE_SYS;
-
- error = acl_get(dip, ACL_DEFAULT, &acl, NULL,
- &er.er_data, &er.er_data_len);
+ error = acl_get(dip, GFS2_POSIX_ACL_DEFAULT, &acl, &el, &data, &len);
+ brelse(el.el_bh);
if (error)
return error;
if (!acl) {
@@ -229,9 +217,8 @@ int gfs2_acl_create(struct gfs2_inode *dip, struct gfs2_inode *ip)
acl = clone;
if (S_ISDIR(ip->i_inode.i_mode)) {
- er.er_name = GFS2_POSIX_ACL_DEFAULT;
- er.er_name_len = GFS2_POSIX_ACL_DEFAULT_LEN;
- error = gfs2_system_eaops.eo_set(ip, &er);
+ error = gfs2_xattr_set(&ip->i_inode, GFS2_EATYPE_SYS,
+ GFS2_POSIX_ACL_DEFAULT, data, len, 0);
if (error)
goto out;
}
@@ -239,21 +226,19 @@ int gfs2_acl_create(struct gfs2_inode *dip, struct gfs2_inode *ip)
error = posix_acl_create_masq(acl, &mode);
if (error < 0)
goto out;
- if (error > 0) {
- er.er_name = GFS2_POSIX_ACL_ACCESS;
- er.er_name_len = GFS2_POSIX_ACL_ACCESS_LEN;
- posix_acl_to_xattr(acl, er.er_data, er.er_data_len);
- er.er_mode = mode;
- er.er_flags = GFS2_ERF_MODE;
- error = gfs2_system_eaops.eo_set(ip, &er);
- if (error)
- goto out;
- } else
- munge_mode(ip, mode);
+ if (error == 0)
+ goto munge;
+ posix_acl_to_xattr(acl, data, len);
+ error = gfs2_xattr_set(&ip->i_inode, GFS2_EATYPE_SYS,
+ GFS2_POSIX_ACL_ACCESS, data, len, 0);
+ if (error)
+ goto out;
+munge:
+ error = munge_mode(ip, mode);
out:
posix_acl_release(acl);
- kfree(er.er_data);
+ kfree(data);
return error;
}
@@ -265,9 +250,9 @@ int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr)
unsigned int len;
int error;
- error = acl_get(ip, ACL_ACCESS, &acl, &el, &data, &len);
+ error = acl_get(ip, GFS2_POSIX_ACL_ACCESS, &acl, &el, &data, &len);
if (error)
- return error;
+ goto out_brelse;
if (!acl)
return gfs2_setattr_simple(ip, attr);
@@ -286,8 +271,9 @@ int gfs2_acl_chmod(struct gfs2_inode *ip, struct iattr *attr)
out:
posix_acl_release(acl);
- brelse(el.el_bh);
kfree(data);
+out_brelse:
+ brelse(el.el_bh);
return error;
}
diff --git a/fs/gfs2/dentry.c b/fs/gfs2/dentry.c
index 022c66cd560..91beddadd38 100644
--- a/fs/gfs2/dentry.c
+++ b/fs/gfs2/dentry.c
@@ -107,8 +107,26 @@ static int gfs2_dhash(struct dentry *dentry, struct qstr *str)
return 0;
}
+static int gfs2_dentry_delete(struct dentry *dentry)
+{
+ struct gfs2_inode *ginode;
+
+ if (!dentry->d_inode)
+ return 0;
+
+ ginode = GFS2_I(dentry->d_inode);
+ if (!ginode->i_iopen_gh.gh_gl)
+ return 0;
+
+ if (test_bit(GLF_DEMOTE, &ginode->i_iopen_gh.gh_gl->gl_flags))
+ return 1;
+
+ return 0;
+}
+
const struct dentry_operations gfs2_dops = {
.d_revalidate = gfs2_drevalidate,
.d_hash = gfs2_dhash,
+ .d_delete = gfs2_dentry_delete,
};
diff --git a/fs/gfs2/eaops.c b/fs/gfs2/eaops.c
deleted file mode 100644
index dee9b03e5b3..00000000000
--- a/fs/gfs2/eaops.c
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
- * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License version 2.
- */
-
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/completion.h>
-#include <linux/buffer_head.h>
-#include <linux/capability.h>
-#include <linux/xattr.h>
-#include <linux/gfs2_ondisk.h>
-#include <asm/uaccess.h>
-
-#include "gfs2.h"
-#include "incore.h"
-#include "acl.h"
-#include "eaops.h"
-#include "eattr.h"
-#include "util.h"
-
-/**
- * gfs2_ea_name2type - get the type of the ea, and truncate type from the name
- * @namep: ea name, possibly with type appended
- *
- * Returns: GFS2_EATYPE_XXX
- */
-
-unsigned int gfs2_ea_name2type(const char *name, const char **truncated_name)
-{
- unsigned int type;
-
- if (strncmp(name, "system.", 7) == 0) {
- type = GFS2_EATYPE_SYS;
- if (truncated_name)
- *truncated_name = name + sizeof("system.") - 1;
- } else if (strncmp(name, "user.", 5) == 0) {
- type = GFS2_EATYPE_USR;
- if (truncated_name)
- *truncated_name = name + sizeof("user.") - 1;
- } else if (strncmp(name, "security.", 9) == 0) {
- type = GFS2_EATYPE_SECURITY;
- if (truncated_name)
- *truncated_name = name + sizeof("security.") - 1;
- } else {
- type = GFS2_EATYPE_UNUSED;
- if (truncated_name)
- *truncated_name = NULL;
- }
-
- return type;
-}
-
-static int system_eo_get(struct gfs2_inode *ip, struct gfs2_ea_request *er)
-{
- if (!GFS2_ACL_IS_ACCESS(er->er_name, er->er_name_len) &&
- !GFS2_ACL_IS_DEFAULT(er->er_name, er->er_name_len) &&
- !capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- if (GFS2_SB(&ip->i_inode)->sd_args.ar_posix_acl == 0 &&
- (GFS2_ACL_IS_ACCESS(er->er_name, er->er_name_len) ||
- GFS2_ACL_IS_DEFAULT(er->er_name, er->er_name_len)))
- return -EOPNOTSUPP;
-
- return gfs2_ea_get_i(ip, er);
-}
-
-static int system_eo_set(struct gfs2_inode *ip, struct gfs2_ea_request *er)
-{
- int remove = 0;
- int error;
-
- if (GFS2_ACL_IS_ACCESS(er->er_name, er->er_name_len)) {
- if (!(er->er_flags & GFS2_ERF_MODE)) {
- er->er_mode = ip->i_inode.i_mode;
- er->er_flags |= GFS2_ERF_MODE;
- }
- error = gfs2_acl_validate_set(ip, 1, er,
- &remove, &er->er_mode);
- if (error)
- return error;
- error = gfs2_ea_set_i(ip, er);
- if (error)
- return error;
- if (remove)
- gfs2_ea_remove_i(ip, er);
- return 0;
-
- } else if (GFS2_ACL_IS_DEFAULT(er->er_name, er->er_name_len)) {
- error = gfs2_acl_validate_set(ip, 0, er,
- &remove, NULL);
- if (error)
- return error;
- if (!remove)
- error = gfs2_ea_set_i(ip, er);
- else {
- error = gfs2_ea_remove_i(ip, er);
- if (error == -ENODATA)
- error = 0;
- }
- return error;
- }
-
- return -EPERM;
-}
-
-static int system_eo_remove(struct gfs2_inode *ip, struct gfs2_ea_request *er)
-{
- if (GFS2_ACL_IS_ACCESS(er->er_name, er->er_name_len)) {
- int error = gfs2_acl_validate_remove(ip, 1);
- if (error)
- return error;
-
- } else if (GFS2_ACL_IS_DEFAULT(er->er_name, er->er_name_len)) {
- int error = gfs2_acl_validate_remove(ip, 0);
- if (error)
- return error;
-
- } else
- return -EPERM;
-
- return gfs2_ea_remove_i(ip, er);
-}
-
-static const struct gfs2_eattr_operations gfs2_user_eaops = {
- .eo_get = gfs2_ea_get_i,
- .eo_set = gfs2_ea_set_i,
- .eo_remove = gfs2_ea_remove_i,
- .eo_name = "user",
-};
-
-const struct gfs2_eattr_operations gfs2_system_eaops = {
- .eo_get = system_eo_get,
- .eo_set = system_eo_set,
- .eo_remove = system_eo_remove,
- .eo_name = "system",
-};
-
-static const struct gfs2_eattr_operations gfs2_security_eaops = {
- .eo_get = gfs2_ea_get_i,
- .eo_set = gfs2_ea_set_i,
- .eo_remove = gfs2_ea_remove_i,
- .eo_name = "security",
-};
-
-const struct gfs2_eattr_operations *gfs2_ea_ops[] = {
- NULL,
- &gfs2_user_eaops,
- &gfs2_system_eaops,
- &gfs2_security_eaops,
-};
-
diff --git a/fs/gfs2/eaops.h b/fs/gfs2/eaops.h
deleted file mode 100644
index da2f7fbbb40..00000000000
--- a/fs/gfs2/eaops.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
- * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
- *
- * This copyrighted material is made available to anyone wishing to use,
- * modify, copy, or redistribute it subject to the terms and conditions
- * of the GNU General Public License version 2.
- */
-
-#ifndef __EAOPS_DOT_H__
-#define __EAOPS_DOT_H__
-
-struct gfs2_ea_request;
-struct gfs2_inode;
-
-struct gfs2_eattr_operations {
- int (*eo_get) (struct gfs2_inode *ip, struct gfs2_ea_request *er);
- int (*eo_set) (struct gfs2_inode *ip, struct gfs2_ea_request *er);
- int (*eo_remove) (struct gfs2_inode *ip, struct gfs2_ea_request *er);
- char *eo_name;
-};
-
-unsigned int gfs2_ea_name2type(const char *name, const char **truncated_name);
-
-extern const struct gfs2_eattr_operations gfs2_system_eaops;
-
-extern const struct gfs2_eattr_operations *gfs2_ea_ops[];
-
-#endif /* __EAOPS_DOT_H__ */
-
diff --git a/fs/gfs2/export.c b/fs/gfs2/export.c
index 9200ef22171..d15876e9aa2 100644
--- a/fs/gfs2/export.c
+++ b/fs/gfs2/export.c
@@ -143,17 +143,14 @@ static struct dentry *gfs2_get_parent(struct dentry *child)
}
static struct dentry *gfs2_get_dentry(struct super_block *sb,
- struct gfs2_inum_host *inum)
+ struct gfs2_inum_host *inum)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
- struct gfs2_holder i_gh, ri_gh, rgd_gh;
- struct gfs2_rgrpd *rgd;
+ struct gfs2_holder i_gh;
struct inode *inode;
struct dentry *dentry;
int error;
- /* System files? */
-
inode = gfs2_ilookup(sb, inum->no_addr);
if (inode) {
if (GFS2_I(inode)->i_no_formal_ino != inum->no_formal_ino) {
@@ -168,29 +165,11 @@ static struct dentry *gfs2_get_dentry(struct super_block *sb,
if (error)
return ERR_PTR(error);
- error = gfs2_rindex_hold(sdp, &ri_gh);
+ error = gfs2_check_blk_type(sdp, inum->no_addr, GFS2_BLKST_DINODE);
if (error)
goto fail;
- error = -EINVAL;
- rgd = gfs2_blk2rgrpd(sdp, inum->no_addr);
- if (!rgd)
- goto fail_rindex;
-
- error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_SHARED, 0, &rgd_gh);
- if (error)
- goto fail_rindex;
-
- error = -ESTALE;
- if (gfs2_get_block_type(rgd, inum->no_addr) != GFS2_BLKST_DINODE)
- goto fail_rgd;
-
- gfs2_glock_dq_uninit(&rgd_gh);
- gfs2_glock_dq_uninit(&ri_gh);
-
- inode = gfs2_inode_lookup(sb, DT_UNKNOWN,
- inum->no_addr,
- 0, 0);
+ inode = gfs2_inode_lookup(sb, DT_UNKNOWN, inum->no_addr, 0, 0);
if (IS_ERR(inode)) {
error = PTR_ERR(inode);
goto fail;
@@ -224,13 +203,6 @@ out_inode:
if (!IS_ERR(dentry))
dentry->d_op = &gfs2_dops;
return dentry;
-
-fail_rgd:
- gfs2_glock_dq_uninit(&rgd_gh);
-
-fail_rindex:
- gfs2_glock_dq_uninit(&ri_gh);
-
fail:
gfs2_glock_dq_uninit(&i_gh);
return ERR_PTR(error);
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 73318a3ce6f..166f38fbd24 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -38,7 +38,6 @@
#include "rgrp.h"
#include "trans.h"
#include "util.h"
-#include "eaops.h"
/**
* gfs2_llseek - seek to a location in a file
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index 61801ada36f..6edb423f90b 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -406,6 +406,12 @@ struct gfs2_statfs_change_host {
#define GFS2_DATA_WRITEBACK 1
#define GFS2_DATA_ORDERED 2
+#define GFS2_ERRORS_DEFAULT GFS2_ERRORS_WITHDRAW
+#define GFS2_ERRORS_WITHDRAW 0
+#define GFS2_ERRORS_CONTINUE 1 /* place holder for future feature */
+#define GFS2_ERRORS_RO 2 /* place holder for future feature */
+#define GFS2_ERRORS_PANIC 3
+
struct gfs2_args {
char ar_lockproto[GFS2_LOCKNAME_LEN]; /* Name of the Lock Protocol */
char ar_locktable[GFS2_LOCKNAME_LEN]; /* Name of the Lock Table */
@@ -422,6 +428,7 @@ struct gfs2_args {
unsigned int ar_data:2; /* ordered/writeback */
unsigned int ar_meta:1; /* mount metafs */
unsigned int ar_discard:1; /* discard requests */
+ unsigned int ar_errors:2; /* errors=withdraw | panic */
int ar_commit; /* Commit interval */
};
@@ -489,7 +496,6 @@ struct gfs2_sb_host {
*/
struct lm_lockstruct {
- u32 ls_id;
unsigned int ls_jid;
unsigned int ls_first;
unsigned int ls_first_done;
@@ -541,18 +547,12 @@ struct gfs2_sbd {
struct dentry *sd_root_dir;
struct inode *sd_jindex;
- struct inode *sd_inum_inode;
struct inode *sd_statfs_inode;
- struct inode *sd_ir_inode;
struct inode *sd_sc_inode;
struct inode *sd_qc_inode;
struct inode *sd_rindex;
struct inode *sd_quota_inode;
- /* Inum stuff */
-
- struct mutex sd_inum_mutex;
-
/* StatFS stuff */
spinlock_t sd_statfs_spin;
@@ -580,7 +580,6 @@ struct gfs2_sbd {
struct gfs2_holder sd_journal_gh;
struct gfs2_holder sd_jinode_gh;
- struct gfs2_holder sd_ir_gh;
struct gfs2_holder sd_sc_gh;
struct gfs2_holder sd_qc_gh;
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 2f94bd72369..fb15d3b1f40 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -24,7 +24,7 @@
#include "acl.h"
#include "bmap.h"
#include "dir.h"
-#include "eattr.h"
+#include "xattr.h"
#include "glock.h"
#include "glops.h"
#include "inode.h"
@@ -519,139 +519,6 @@ out:
return inode ? inode : ERR_PTR(error);
}
-static void gfs2_inum_range_in(struct gfs2_inum_range_host *ir, const void *buf)
-{
- const struct gfs2_inum_range *str = buf;
-
- ir->ir_start = be64_to_cpu(str->ir_start);
- ir->ir_length = be64_to_cpu(str->ir_length);
-}
-
-static void gfs2_inum_range_out(const struct gfs2_inum_range_host *ir, void *buf)
-{
- struct gfs2_inum_range *str = buf;
-
- str->ir_start = cpu_to_be64(ir->ir_start);
- str->ir_length = cpu_to_be64(ir->ir_length);
-}
-
-static int pick_formal_ino_1(struct gfs2_sbd *sdp, u64 *formal_ino)
-{
- struct gfs2_inode *ip = GFS2_I(sdp->sd_ir_inode);
- struct buffer_head *bh;
- struct gfs2_inum_range_host ir;
- int error;
-
- error = gfs2_trans_begin(sdp, RES_DINODE, 0);
- if (error)
- return error;
- mutex_lock(&sdp->sd_inum_mutex);
-
- error = gfs2_meta_inode_buffer(ip, &bh);
- if (error) {
- mutex_unlock(&sdp->sd_inum_mutex);
- gfs2_trans_end(sdp);
- return error;
- }
-
- gfs2_inum_range_in(&ir, bh->b_data + sizeof(struct gfs2_dinode));
-
- if (ir.ir_length) {
- *formal_ino = ir.ir_start++;
- ir.ir_length--;
- gfs2_trans_add_bh(ip->i_gl, bh, 1);
- gfs2_inum_range_out(&ir,
- bh->b_data + sizeof(struct gfs2_dinode));
- brelse(bh);
- mutex_unlock(&sdp->sd_inum_mutex);
- gfs2_trans_end(sdp);
- return 0;
- }
-
- brelse(bh);
-
- mutex_unlock(&sdp->sd_inum_mutex);
- gfs2_trans_end(sdp);
-
- return 1;
-}
-
-static int pick_formal_ino_2(struct gfs2_sbd *sdp, u64 *formal_ino)
-{
- struct gfs2_inode *ip = GFS2_I(sdp->sd_ir_inode);
- struct gfs2_inode *m_ip = GFS2_I(sdp->sd_inum_inode);
- struct gfs2_holder gh;
- struct buffer_head *bh;
- struct gfs2_inum_range_host ir;
- int error;
-
- error = gfs2_glock_nq_init(m_ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
- if (error)
- return error;
-
- error = gfs2_trans_begin(sdp, 2 * RES_DINODE, 0);
- if (error)
- goto out;
- mutex_lock(&sdp->sd_inum_mutex);
-
- error = gfs2_meta_inode_buffer(ip, &bh);
- if (error)
- goto out_end_trans;
-
- gfs2_inum_range_in(&ir, bh->b_data + sizeof(struct gfs2_dinode));
-
- if (!ir.ir_length) {
- struct buffer_head *m_bh;
- u64 x, y;
- __be64 z;
-
- error = gfs2_meta_inode_buffer(m_ip, &m_bh);
- if (error)
- goto out_brelse;
-
- z = *(__be64 *)(m_bh->b_data + sizeof(struct gfs2_dinode));
- x = y = be64_to_cpu(z);
- ir.ir_start = x;
- ir.ir_length = GFS2_INUM_QUANTUM;
- x += GFS2_INUM_QUANTUM;
- if (x < y)
- gfs2_consist_inode(m_ip);
- z = cpu_to_be64(x);
- gfs2_trans_add_bh(m_ip->i_gl, m_bh, 1);
- *(__be64 *)(m_bh->b_data + sizeof(struct gfs2_dinode)) = z;
-
- brelse(m_bh);
- }
-
- *formal_ino = ir.ir_start++;
- ir.ir_length--;
-
- gfs2_trans_add_bh(ip->i_gl, bh, 1);
- gfs2_inum_range_out(&ir, bh->b_data + sizeof(struct gfs2_dinode));
-
-out_brelse:
- brelse(bh);
-out_end_trans:
- mutex_unlock(&sdp->sd_inum_mutex);
- gfs2_trans_end(sdp);
-out:
- gfs2_glock_dq_uninit(&gh);
- return error;
-}
-
-static int pick_formal_ino(struct gfs2_sbd *sdp, u64 *inum)
-{
- int error;
-
- error = pick_formal_ino_1(sdp, inum);
- if (error <= 0)
- return error;
-
- error = pick_formal_ino_2(sdp, inum);
-
- return error;
-}
-
/**
* create_ok - OK to create a new on-disk inode here?
* @dip: Directory in which dinode is to be created
@@ -731,7 +598,7 @@ static int alloc_dinode(struct gfs2_inode *dip, u64 *no_addr, u64 *generation)
if (error)
goto out_ipreserv;
- *no_addr = gfs2_alloc_di(dip, generation);
+ error = gfs2_alloc_di(dip, no_addr, generation);
gfs2_trans_end(sdp);
@@ -924,7 +791,6 @@ static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip)
size_t len;
void *value;
char *name;
- struct gfs2_ea_request er;
err = security_inode_init_security(&ip->i_inode, &dip->i_inode,
&name, &value, &len);
@@ -935,16 +801,7 @@ static int gfs2_security_init(struct gfs2_inode *dip, struct gfs2_inode *ip)
return err;
}
- memset(&er, 0, sizeof(struct gfs2_ea_request));
-
- er.er_type = GFS2_EATYPE_SECURITY;
- er.er_name = name;
- er.er_data = value;
- er.er_name_len = strlen(name);
- er.er_data_len = len;
-
- err = gfs2_ea_set_i(ip, &er);
-
+ err = gfs2_xattr_set(&ip->i_inode, GFS2_EATYPE_SECURITY, name, value, len, 0);
kfree(value);
kfree(name);
@@ -991,13 +848,10 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name,
if (error)
goto fail_gunlock;
- error = pick_formal_ino(sdp, &inum.no_formal_ino);
- if (error)
- goto fail_gunlock;
-
error = alloc_dinode(dip, &inum.no_addr, &generation);
if (error)
goto fail_gunlock;
+ inum.no_formal_ino = generation;
error = gfs2_glock_nq_num(sdp, inum.no_addr, &gfs2_inode_glops,
LM_ST_EXCLUSIVE, GL_SKIP, ghs + 1);
@@ -1008,9 +862,8 @@ struct inode *gfs2_createi(struct gfs2_holder *ghs, const struct qstr *name,
if (error)
goto fail_gunlock2;
- inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode),
- inum.no_addr,
- inum.no_formal_ino, 0);
+ inode = gfs2_inode_lookup(dir->i_sb, IF2DT(mode), inum.no_addr,
+ inum.no_formal_ino, 0);
if (IS_ERR(inode))
goto fail_gunlock2;
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index 7bc3c45cd67..52fb6c04898 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -84,7 +84,6 @@ static struct gfs2_sbd *init_sbd(struct super_block *sb)
gfs2_tune_init(&sdp->sd_tune);
- mutex_init(&sdp->sd_inum_mutex);
spin_lock_init(&sdp->sd_statfs_spin);
spin_lock_init(&sdp->sd_rindex_spin);
@@ -833,21 +832,12 @@ static int init_inodes(struct gfs2_sbd *sdp, int undo)
if (error)
goto fail;
- /* Read in the master inode number inode */
- sdp->sd_inum_inode = gfs2_lookup_simple(master, "inum");
- if (IS_ERR(sdp->sd_inum_inode)) {
- error = PTR_ERR(sdp->sd_inum_inode);
- fs_err(sdp, "can't read in inum inode: %d\n", error);
- goto fail_journal;
- }
-
-
/* Read in the master statfs inode */
sdp->sd_statfs_inode = gfs2_lookup_simple(master, "statfs");
if (IS_ERR(sdp->sd_statfs_inode)) {
error = PTR_ERR(sdp->sd_statfs_inode);
fs_err(sdp, "can't read in statfs inode: %d\n", error);
- goto fail_inum;
+ goto fail_journal;
}
/* Read in the resource index inode */
@@ -876,8 +866,6 @@ fail_rindex:
iput(sdp->sd_rindex);
fail_statfs:
iput(sdp->sd_statfs_inode);
-fail_inum:
- iput(sdp->sd_inum_inode);
fail_journal:
init_journal(sdp, UNDO);
fail:
@@ -905,20 +893,12 @@ static int init_per_node(struct gfs2_sbd *sdp, int undo)
return error;
}
- sprintf(buf, "inum_range%u", sdp->sd_jdesc->jd_jid);
- sdp->sd_ir_inode = gfs2_lookup_simple(pn, buf);
- if (IS_ERR(sdp->sd_ir_inode)) {
- error = PTR_ERR(sdp->sd_ir_inode);
- fs_err(sdp, "can't find local \"ir\" file: %d\n", error);
- goto fail;
- }
-
sprintf(buf, "statfs_change%u", sdp->sd_jdesc->jd_jid);
sdp->sd_sc_inode = gfs2_lookup_simple(pn, buf);
if (IS_ERR(sdp->sd_sc_inode)) {
error = PTR_ERR(sdp->sd_sc_inode);
fs_err(sdp, "can't find local \"sc\" file: %d\n", error);
- goto fail_ir_i;
+ goto fail;
}
sprintf(buf, "quota_change%u", sdp->sd_jdesc->jd_jid);
@@ -932,27 +912,16 @@ static int init_per_node(struct gfs2_sbd *sdp, int undo)
iput(pn);
pn = NULL;
- ip = GFS2_I(sdp->sd_ir_inode);
- error = gfs2_glock_nq_init(ip->i_gl,
- LM_ST_EXCLUSIVE, 0,
- &sdp->sd_ir_gh);
- if (error) {
- fs_err(sdp, "can't lock local \"ir\" file: %d\n", error);
- goto fail_qc_i;
- }
-
ip = GFS2_I(sdp->sd_sc_inode);
- error = gfs2_glock_nq_init(ip->i_gl,
- LM_ST_EXCLUSIVE, 0,
+ error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0,
&sdp->sd_sc_gh);
if (error) {
fs_err(sdp, "can't lock local \"sc\" file: %d\n", error);
- goto fail_ir_gh;
+ goto fail_qc_i;
}
ip = GFS2_I(sdp->sd_qc_inode);
- error = gfs2_glock_nq_init(ip->i_gl,
- LM_ST_EXCLUSIVE, 0,
+ error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0,
&sdp->sd_qc_gh);
if (error) {
fs_err(sdp, "can't lock local \"qc\" file: %d\n", error);
@@ -965,14 +934,10 @@ fail_qc_gh:
gfs2_glock_dq_uninit(&sdp->sd_qc_gh);
fail_ut_gh:
gfs2_glock_dq_uninit(&sdp->sd_sc_gh);
-fail_ir_gh:
- gfs2_glock_dq_uninit(&sdp->sd_ir_gh);
fail_qc_i:
iput(sdp->sd_qc_inode);
fail_ut_i:
iput(sdp->sd_sc_inode);
-fail_ir_i:
- iput(sdp->sd_ir_inode);
fail:
if (pn)
iput(pn);
@@ -1063,7 +1028,6 @@ static int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent)
ls->ls_ops = lm;
ls->ls_first = 1;
- ls->ls_id = 0;
for (options = args->ar_hostdata; (o = strsep(&options, ":")); ) {
substring_t tmp[MAX_OPT_ARGS];
@@ -1081,10 +1045,7 @@ static int gfs2_lm_mount(struct gfs2_sbd *sdp, int silent)
ls->ls_jid = option;
break;
case Opt_id:
- ret = match_int(&tmp[0], &option);
- if (ret)
- goto hostdata_error;
- ls->ls_id = option;
+ /* Obsolete, but left for backward compat purposes */
break;
case Opt_first:
ret = match_int(&tmp[0], &option);
@@ -1133,6 +1094,17 @@ void gfs2_lm_unmount(struct gfs2_sbd *sdp)
lm->lm_unmount(sdp);
}
+void gfs2_online_uevent(struct gfs2_sbd *sdp)
+{
+ struct super_block *sb = sdp->sd_vfs;
+ char ro[20];
+ char spectator[20];
+ char *envp[] = { ro, spectator, NULL };
+ sprintf(ro, "RDONLY=%d", (sb->s_flags & MS_RDONLY) ? 1 : 0);
+ sprintf(spectator, "SPECTATOR=%d", sdp->sd_args.ar_spectator ? 1 : 0);
+ kobject_uevent_env(&sdp->sd_kobj, KOBJ_ONLINE, envp);
+}
+
/**
* fill_super - Read in superblock
* @sb: The VFS superblock
@@ -1157,6 +1129,7 @@ static int fill_super(struct super_block *sb, void *data, int silent)
sdp->sd_args.ar_quota = GFS2_QUOTA_DEFAULT;
sdp->sd_args.ar_data = GFS2_DATA_DEFAULT;
sdp->sd_args.ar_commit = 60;
+ sdp->sd_args.ar_errors = GFS2_ERRORS_DEFAULT;
error = gfs2_mount_args(sdp, &sdp->sd_args, data);
if (error) {
@@ -1174,6 +1147,7 @@ static int fill_super(struct super_block *sb, void *data, int silent)
sb->s_magic = GFS2_MAGIC;
sb->s_op = &gfs2_super_ops;
sb->s_export_op = &gfs2_export_ops;
+ sb->s_xattr = gfs2_xattr_handlers;
sb->s_time_gran = 1;
sb->s_maxbytes = MAX_LFS_FILESIZE;
@@ -1236,7 +1210,7 @@ static int fill_super(struct super_block *sb, void *data, int silent)
}
gfs2_glock_dq_uninit(&mount_gh);
-
+ gfs2_online_uevent(sdp);
return 0;
fail_threads:
diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c
index f8bd20baf99..c3ac1805405 100644
--- a/fs/gfs2/ops_inode.c
+++ b/fs/gfs2/ops_inode.c
@@ -26,8 +26,7 @@
#include "acl.h"
#include "bmap.h"
#include "dir.h"
-#include "eaops.h"
-#include "eattr.h"
+#include "xattr.h"
#include "glock.h"
#include "inode.h"
#include "meta_io.h"
@@ -349,7 +348,7 @@ static int gfs2_unlink(struct inode *dir, struct dentry *dentry)
error = gfs2_trans_begin(sdp, 2*RES_DINODE + RES_LEAF + RES_RG_BIT, 0);
if (error)
- goto out_rgrp;
+ goto out_gunlock;
error = gfs2_dir_del(dip, &dentry->d_name);
if (error)
@@ -1302,60 +1301,53 @@ static int gfs2_setxattr(struct dentry *dentry, const char *name,
const void *data, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
- struct gfs2_ea_request er;
-
- memset(&er, 0, sizeof(struct gfs2_ea_request));
- er.er_type = gfs2_ea_name2type(name, &er.er_name);
- if (er.er_type == GFS2_EATYPE_UNUSED)
- return -EOPNOTSUPP;
- er.er_data = (char *)data;
- er.er_name_len = strlen(er.er_name);
- er.er_data_len = size;
- er.er_flags = flags;
-
- gfs2_assert_warn(GFS2_SB(inode), !(er.er_flags & GFS2_ERF_MODE));
+ struct gfs2_inode *ip = GFS2_I(inode);
+ struct gfs2_holder gh;
+ int ret;
- return gfs2_ea_set(GFS2_I(inode), &er);
+ gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
+ ret = gfs2_glock_nq(&gh);
+ if (ret == 0) {
+ ret = generic_setxattr(dentry, name, data, size, flags);
+ gfs2_glock_dq(&gh);
+ }
+ gfs2_holder_uninit(&gh);
+ return ret;
}
static ssize_t gfs2_getxattr(struct dentry *dentry, const char *name,
void *data, size_t size)
{
- struct gfs2_ea_request er;
-
- memset(&er, 0, sizeof(struct gfs2_ea_request));
- er.er_type = gfs2_ea_name2type(name, &er.er_name);
- if (er.er_type == GFS2_EATYPE_UNUSED)
- return -EOPNOTSUPP;
- er.er_data = data;
- er.er_name_len = strlen(er.er_name);
- er.er_data_len = size;
-
- return gfs2_ea_get(GFS2_I(dentry->d_inode), &er);
-}
-
-static ssize_t gfs2_listxattr(struct dentry *dentry, char *buffer, size_t size)
-{
- struct gfs2_ea_request er;
-
- memset(&er, 0, sizeof(struct gfs2_ea_request));
- er.er_data = (size) ? buffer : NULL;
- er.er_data_len = size;
+ struct inode *inode = dentry->d_inode;
+ struct gfs2_inode *ip = GFS2_I(inode);
+ struct gfs2_holder gh;
+ int ret;
- return gfs2_ea_list(GFS2_I(dentry->d_inode), &er);
+ gfs2_holder_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
+ ret = gfs2_glock_nq(&gh);
+ if (ret == 0) {
+ ret = generic_getxattr(dentry, name, data, size);
+ gfs2_glock_dq(&gh);
+ }
+ gfs2_holder_uninit(&gh);
+ return ret;
}
static int gfs2_removexattr(struct dentry *dentry, const char *name)
{
- struct gfs2_ea_request er;
-
- memset(&er, 0, sizeof(struct gfs2_ea_request));
- er.er_type = gfs2_ea_name2type(name, &er.er_name);
- if (er.er_type == GFS2_EATYPE_UNUSED)
- return -EOPNOTSUPP;
- er.er_name_len = strlen(er.er_name);
+ struct inode *inode = dentry->d_inode;
+ struct gfs2_inode *ip = GFS2_I(inode);
+ struct gfs2_holder gh;
+ int ret;
- return gfs2_ea_remove(GFS2_I(dentry->d_inode), &er);
+ gfs2_holder_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &gh);
+ ret = gfs2_glock_nq(&gh);
+ if (ret == 0) {
+ ret = generic_removexattr(dentry, name);
+ gfs2_glock_dq(&gh);
+ }
+ gfs2_holder_uninit(&gh);
+ return ret;
}
static int gfs2_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index fba795798d3..28c590b7c9d 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -857,7 +857,8 @@ static void gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset,
goto start_new_extent;
if ((start + nr_sects) != blk) {
rv = blkdev_issue_discard(bdev, start,
- nr_sects, GFP_NOFS);
+ nr_sects, GFP_NOFS,
+ DISCARD_FL_BARRIER);
if (rv)
goto fail;
nr_sects = 0;
@@ -871,7 +872,8 @@ start_new_extent:
}
}
if (nr_sects) {
- rv = blkdev_issue_discard(bdev, start, nr_sects, GFP_NOFS);
+ rv = blkdev_issue_discard(bdev, start, nr_sects, GFP_NOFS,
+ DISCARD_FL_BARRIER);
if (rv)
goto fail;
}
@@ -1256,7 +1258,7 @@ void gfs2_inplace_release(struct gfs2_inode *ip)
* Returns: The block type (GFS2_BLKST_*)
*/
-unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block)
+static unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block)
{
struct gfs2_bitmap *bi = NULL;
u32 length, rgrp_block, buf_block;
@@ -1459,6 +1461,16 @@ int gfs2_rgrp_dump(struct seq_file *seq, const struct gfs2_glock *gl)
return 0;
}
+static void gfs2_rgrp_error(struct gfs2_rgrpd *rgd)
+{
+ struct gfs2_sbd *sdp = rgd->rd_sbd;
+ fs_warn(sdp, "rgrp %llu has an error, marking it readonly until umount\n",
+ (unsigned long long)rgd->rd_addr);
+ fs_warn(sdp, "umount on all nodes and run fsck.gfs2 to fix the error\n");
+ gfs2_rgrp_dump(NULL, rgd->rd_gl);
+ rgd->rd_flags |= GFS2_RDF_ERROR;
+}
+
/**
* gfs2_alloc_block - Allocate one or more blocks
* @ip: the inode to allocate the block for
@@ -1520,22 +1532,20 @@ int gfs2_alloc_block(struct gfs2_inode *ip, u64 *bn, unsigned int *n)
return 0;
rgrp_error:
- fs_warn(sdp, "rgrp %llu has an error, marking it readonly until umount\n",
- (unsigned long long)rgd->rd_addr);
- fs_warn(sdp, "umount on all nodes and run fsck.gfs2 to fix the error\n");
- gfs2_rgrp_dump(NULL, rgd->rd_gl);
- rgd->rd_flags |= GFS2_RDF_ERROR;
+ gfs2_rgrp_error(rgd);
return -EIO;
}
/**
* gfs2_alloc_di - Allocate a dinode
* @dip: the directory that the inode is going in
+ * @bn: the block number which is allocated
+ * @generation: the generation number of the inode
*
- * Returns: the block allocated
+ * Returns: 0 on success or error
*/
-u64 gfs2_alloc_di(struct gfs2_inode *dip, u64 *generation)
+int gfs2_alloc_di(struct gfs2_inode *dip, u64 *bn, u64 *generation)
{
struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
struct gfs2_alloc *al = dip->i_alloc;
@@ -1546,16 +1556,21 @@ u64 gfs2_alloc_di(struct gfs2_inode *dip, u64 *generation)
blk = rgblk_search(rgd, rgd->rd_last_alloc,
GFS2_BLKST_FREE, GFS2_BLKST_DINODE, &n);
- BUG_ON(blk == BFITNOENT);
- rgd->rd_last_alloc = blk;
+ /* Since all blocks are reserved in advance, this shouldn't happen */
+ if (blk == BFITNOENT)
+ goto rgrp_error;
+ rgd->rd_last_alloc = blk;
block = rgd->rd_data0 + blk;
+ if (rgd->rd_free == 0)
+ goto rgrp_error;
- gfs2_assert_withdraw(sdp, rgd->rd_free);
rgd->rd_free--;
rgd->rd_dinodes++;
*generation = rgd->rd_igeneration++;
+ if (*generation == 0)
+ *generation = rgd->rd_igeneration++;
gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1);
gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data);
@@ -1568,7 +1583,12 @@ u64 gfs2_alloc_di(struct gfs2_inode *dip, u64 *generation)
rgd->rd_free_clone--;
spin_unlock(&sdp->sd_rindex_spin);
trace_gfs2_block_alloc(dip, block, 1, GFS2_BLKST_DINODE);
- return block;
+ *bn = block;
+ return 0;
+
+rgrp_error:
+ gfs2_rgrp_error(rgd);
+ return -EIO;
}
/**
@@ -1676,6 +1696,46 @@ void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
}
/**
+ * gfs2_check_blk_type - Check the type of a block
+ * @sdp: The superblock
+ * @no_addr: The block number to check
+ * @type: The block type we are looking for
+ *
+ * Returns: 0 if the block type matches the expected type
+ * -ESTALE if it doesn't match
+ * or -ve errno if something went wrong while checking
+ */
+
+int gfs2_check_blk_type(struct gfs2_sbd *sdp, u64 no_addr, unsigned int type)
+{
+ struct gfs2_rgrpd *rgd;
+ struct gfs2_holder ri_gh, rgd_gh;
+ int error;
+
+ error = gfs2_rindex_hold(sdp, &ri_gh);
+ if (error)
+ goto fail;
+
+ error = -EINVAL;
+ rgd = gfs2_blk2rgrpd(sdp, no_addr);
+ if (!rgd)
+ goto fail_rindex;
+
+ error = gfs2_glock_nq_init(rgd->rd_gl, LM_ST_SHARED, 0, &rgd_gh);
+ if (error)
+ goto fail_rindex;
+
+ if (gfs2_get_block_type(rgd, no_addr) != type)
+ error = -ESTALE;
+
+ gfs2_glock_dq_uninit(&rgd_gh);
+fail_rindex:
+ gfs2_glock_dq_uninit(&ri_gh);
+fail:
+ return error;
+}
+
+/**
* gfs2_rlist_add - add a RG to a list of RGs
* @sdp: the filesystem
* @rlist: the list of resource groups
diff --git a/fs/gfs2/rgrp.h b/fs/gfs2/rgrp.h
index 1e76ff0f3e0..b4106ddaaa9 100644
--- a/fs/gfs2/rgrp.h
+++ b/fs/gfs2/rgrp.h
@@ -44,15 +44,15 @@ gfs2_inplace_reserve_i((ip), __FILE__, __LINE__)
extern void gfs2_inplace_release(struct gfs2_inode *ip);
-extern unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block);
-
extern int gfs2_alloc_block(struct gfs2_inode *ip, u64 *bn, unsigned int *n);
-extern u64 gfs2_alloc_di(struct gfs2_inode *ip, u64 *generation);
+extern int gfs2_alloc_di(struct gfs2_inode *ip, u64 *bn, u64 *generation);
extern void gfs2_free_data(struct gfs2_inode *ip, u64 bstart, u32 blen);
extern void gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen);
extern void gfs2_free_di(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip);
extern void gfs2_unlink_di(struct inode *inode);
+extern int gfs2_check_blk_type(struct gfs2_sbd *sdp, u64 no_addr,
+ unsigned int type);
struct gfs2_rgrp_list {
unsigned int rl_rgrps;
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index f522bb01797..0ec3ec672de 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -38,7 +38,7 @@
#include "trans.h"
#include "util.h"
#include "sys.h"
-#include "eattr.h"
+#include "xattr.h"
#define args_neq(a1, a2, x) ((a1)->ar_##x != (a2)->ar_##x)
@@ -68,6 +68,8 @@ enum {
Opt_discard,
Opt_nodiscard,
Opt_commit,
+ Opt_err_withdraw,
+ Opt_err_panic,
Opt_error,
};
@@ -97,6 +99,8 @@ static const match_table_t tokens = {
{Opt_discard, "discard"},
{Opt_nodiscard, "nodiscard"},
{Opt_commit, "commit=%d"},
+ {Opt_err_withdraw, "errors=withdraw"},
+ {Opt_err_panic, "errors=panic"},
{Opt_error, NULL}
};
@@ -152,6 +156,11 @@ int gfs2_mount_args(struct gfs2_sbd *sdp, struct gfs2_args *args, char *options)
args->ar_localcaching = 1;
break;
case Opt_debug:
+ if (args->ar_errors == GFS2_ERRORS_PANIC) {
+ fs_info(sdp, "-o debug and -o errors=panic "
+ "are mutually exclusive.\n");
+ return -EINVAL;
+ }
args->ar_debug = 1;
break;
case Opt_nodebug:
@@ -205,6 +214,17 @@ int gfs2_mount_args(struct gfs2_sbd *sdp, struct gfs2_args *args, char *options)
return rv ? rv : -EINVAL;
}
break;
+ case Opt_err_withdraw:
+ args->ar_errors = GFS2_ERRORS_WITHDRAW;
+ break;
+ case Opt_err_panic:
+ if (args->ar_debug) {
+ fs_info(sdp, "-o debug and -o errors=panic "
+ "are mutually exclusive.\n");
+ return -EINVAL;
+ }
+ args->ar_errors = GFS2_ERRORS_PANIC;
+ break;
case Opt_error:
default:
fs_info(sdp, "invalid mount option: %s\n", o);
@@ -768,7 +788,6 @@ restart:
/* Release stuff */
iput(sdp->sd_jindex);
- iput(sdp->sd_inum_inode);
iput(sdp->sd_statfs_inode);
iput(sdp->sd_rindex);
iput(sdp->sd_quota_inode);
@@ -779,10 +798,8 @@ restart:
if (!sdp->sd_args.ar_spectator) {
gfs2_glock_dq_uninit(&sdp->sd_journal_gh);
gfs2_glock_dq_uninit(&sdp->sd_jinode_gh);
- gfs2_glock_dq_uninit(&sdp->sd_ir_gh);
gfs2_glock_dq_uninit(&sdp->sd_sc_gh);
gfs2_glock_dq_uninit(&sdp->sd_qc_gh);
- iput(sdp->sd_ir_inode);
iput(sdp->sd_sc_inode);
iput(sdp->sd_qc_inode);
}
@@ -1084,6 +1101,7 @@ static int gfs2_remount_fs(struct super_block *sb, int *flags, char *data)
gt->gt_log_flush_secs = args.ar_commit;
spin_unlock(&gt->gt_spin);
+ gfs2_online_uevent(sdp);
return 0;
}
@@ -1225,6 +1243,22 @@ static int gfs2_show_options(struct seq_file *s, struct vfsmount *mnt)
lfsecs = sdp->sd_tune.gt_log_flush_secs;
if (lfsecs != 60)
seq_printf(s, ",commit=%d", lfsecs);
+ if (args->ar_errors != GFS2_ERRORS_DEFAULT) {
+ const char *state;
+
+ switch (args->ar_errors) {
+ case GFS2_ERRORS_WITHDRAW:
+ state = "withdraw";
+ break;
+ case GFS2_ERRORS_PANIC:
+ state = "panic";
+ break;
+ default:
+ state = "unknown";
+ break;
+ }
+ seq_printf(s, ",errors=%s", state);
+ }
return 0;
}
@@ -1252,6 +1286,10 @@ static void gfs2_delete_inode(struct inode *inode)
goto out;
}
+ error = gfs2_check_blk_type(sdp, ip->i_no_addr, GFS2_BLKST_UNLINKED);
+ if (error)
+ goto out_truncate;
+
gfs2_glock_dq_wait(&ip->i_iopen_gh);
gfs2_holder_reinit(LM_ST_EXCLUSIVE, LM_FLAG_TRY_1CB | GL_NOCACHE, &ip->i_iopen_gh);
error = gfs2_glock_nq(&ip->i_iopen_gh);
diff --git a/fs/gfs2/super.h b/fs/gfs2/super.h
index 22e0417ed99..235db368288 100644
--- a/fs/gfs2/super.h
+++ b/fs/gfs2/super.h
@@ -25,7 +25,7 @@ static inline unsigned int gfs2_jindex_size(struct gfs2_sbd *sdp)
return x;
}
-void gfs2_jindex_free(struct gfs2_sbd *sdp);
+extern void gfs2_jindex_free(struct gfs2_sbd *sdp);
extern int gfs2_mount_args(struct gfs2_sbd *sdp, struct gfs2_args *args, char *data);
@@ -36,7 +36,7 @@ extern int gfs2_lookup_in_master_dir(struct gfs2_sbd *sdp, char *filename,
struct gfs2_inode **ipp);
extern int gfs2_make_fs_rw(struct gfs2_sbd *sdp);
-
+extern void gfs2_online_uevent(struct gfs2_sbd *sdp);
extern int gfs2_statfs_init(struct gfs2_sbd *sdp);
extern void gfs2_statfs_change(struct gfs2_sbd *sdp, s64 total, s64 free,
s64 dinodes);
@@ -54,6 +54,7 @@ extern struct file_system_type gfs2meta_fs_type;
extern const struct export_operations gfs2_export_ops;
extern const struct super_operations gfs2_super_ops;
extern const struct dentry_operations gfs2_dops;
+extern struct xattr_handler *gfs2_xattr_handlers[];
#endif /* __SUPER_DOT_H__ */
diff --git a/fs/gfs2/sys.c b/fs/gfs2/sys.c
index a7cbfbd340c..446329728d5 100644
--- a/fs/gfs2/sys.c
+++ b/fs/gfs2/sys.c
@@ -16,6 +16,7 @@
#include <linux/kobject.h>
#include <asm/uaccess.h>
#include <linux/gfs2_ondisk.h>
+#include <linux/genhd.h>
#include "gfs2.h"
#include "incore.h"
@@ -319,12 +320,6 @@ static ssize_t block_store(struct gfs2_sbd *sdp, const char *buf, size_t len)
return ret;
}
-static ssize_t lkid_show(struct gfs2_sbd *sdp, char *buf)
-{
- struct lm_lockstruct *ls = &sdp->sd_lockstruct;
- return sprintf(buf, "%u\n", ls->ls_id);
-}
-
static ssize_t lkfirst_show(struct gfs2_sbd *sdp, char *buf)
{
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
@@ -389,7 +384,6 @@ static struct gfs2_attr gdlm_attr_##_name = __ATTR(_name,_mode,_show,_store)
GDLM_ATTR(proto_name, 0444, proto_name_show, NULL);
GDLM_ATTR(block, 0644, block_show, block_store);
GDLM_ATTR(withdraw, 0644, withdraw_show, withdraw_store);
-GDLM_ATTR(id, 0444, lkid_show, NULL);
GDLM_ATTR(jid, 0444, jid_show, NULL);
GDLM_ATTR(first, 0444, lkfirst_show, NULL);
GDLM_ATTR(first_done, 0444, first_done_show, NULL);
@@ -401,7 +395,6 @@ static struct attribute *lock_module_attrs[] = {
&gdlm_attr_proto_name.attr,
&gdlm_attr_block.attr,
&gdlm_attr_withdraw.attr,
- &gdlm_attr_id.attr,
&gdlm_attr_jid.attr,
&gdlm_attr_first.attr,
&gdlm_attr_first_done.attr,
@@ -519,7 +512,14 @@ static struct attribute_group lock_module_group = {
int gfs2_sys_fs_add(struct gfs2_sbd *sdp)
{
+ struct super_block *sb = sdp->sd_vfs;
int error;
+ char ro[20];
+ char spectator[20];
+ char *envp[] = { ro, spectator, NULL };
+
+ sprintf(ro, "RDONLY=%d", (sb->s_flags & MS_RDONLY) ? 1 : 0);
+ sprintf(spectator, "SPECTATOR=%d", sdp->sd_args.ar_spectator ? 1 : 0);
sdp->sd_kobj.kset = gfs2_kset;
error = kobject_init_and_add(&sdp->sd_kobj, &gfs2_ktype, NULL,
@@ -535,9 +535,17 @@ int gfs2_sys_fs_add(struct gfs2_sbd *sdp)
if (error)
goto fail_tune;
- kobject_uevent(&sdp->sd_kobj, KOBJ_ADD);
+ error = sysfs_create_link(&sdp->sd_kobj,
+ &disk_to_dev(sb->s_bdev->bd_disk)->kobj,
+ "device");
+ if (error)
+ goto fail_lock_module;
+
+ kobject_uevent_env(&sdp->sd_kobj, KOBJ_ADD, envp);
return 0;
+fail_lock_module:
+ sysfs_remove_group(&sdp->sd_kobj, &lock_module_group);
fail_tune:
sysfs_remove_group(&sdp->sd_kobj, &tune_group);
fail_reg:
@@ -549,12 +557,12 @@ fail:
void gfs2_sys_fs_del(struct gfs2_sbd *sdp)
{
+ sysfs_remove_link(&sdp->sd_kobj, "device");
sysfs_remove_group(&sdp->sd_kobj, &tune_group);
sysfs_remove_group(&sdp->sd_kobj, &lock_module_group);
kobject_put(&sdp->sd_kobj);
}
-
static int gfs2_uevent(struct kset *kset, struct kobject *kobj,
struct kobj_uevent_env *env)
{
@@ -563,6 +571,8 @@ static int gfs2_uevent(struct kset *kset, struct kobject *kobj,
add_uevent_var(env, "LOCKTABLE=%s", sdp->sd_table_name);
add_uevent_var(env, "LOCKPROTO=%s", sdp->sd_proto_name);
+ if (!sdp->sd_args.ar_spectator)
+ add_uevent_var(env, "JOURNALID=%u", sdp->sd_lockstruct.ls_jid);
if (gfs2_uuid_valid(uuid)) {
add_uevent_var(env, "UUID=%02X%02X%02X%02X-%02X%02X-%02X%02X-"
"%02X%02X-%02X%02X%02X%02X%02X%02X",
@@ -578,7 +588,6 @@ static struct kset_uevent_ops gfs2_uevent_ops = {
.uevent = gfs2_uevent,
};
-
int gfs2_sys_init(void)
{
gfs2_kset = kset_create_and_add("gfs2", &gfs2_uevent_ops, fs_kobj);
diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c
index 9d12b1118ba..f6a7efa34eb 100644
--- a/fs/gfs2/util.c
+++ b/fs/gfs2/util.c
@@ -38,24 +38,30 @@ int gfs2_lm_withdraw(struct gfs2_sbd *sdp, char *fmt, ...)
const struct lm_lockops *lm = ls->ls_ops;
va_list args;
- if (test_and_set_bit(SDF_SHUTDOWN, &sdp->sd_flags))
+ if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW &&
+ test_and_set_bit(SDF_SHUTDOWN, &sdp->sd_flags))
return 0;
va_start(args, fmt);
vprintk(fmt, args);
va_end(args);
- fs_err(sdp, "about to withdraw this file system\n");
- BUG_ON(sdp->sd_args.ar_debug);
+ if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW) {
+ fs_err(sdp, "about to withdraw this file system\n");
+ BUG_ON(sdp->sd_args.ar_debug);
- kobject_uevent(&sdp->sd_kobj, KOBJ_OFFLINE);
+ kobject_uevent(&sdp->sd_kobj, KOBJ_OFFLINE);
- if (lm->lm_unmount) {
- fs_err(sdp, "telling LM to unmount\n");
- lm->lm_unmount(sdp);
+ if (lm->lm_unmount) {
+ fs_err(sdp, "telling LM to unmount\n");
+ lm->lm_unmount(sdp);
+ }
+ fs_err(sdp, "withdrawn\n");
+ dump_stack();
}
- fs_err(sdp, "withdrawn\n");
- dump_stack();
+
+ if (sdp->sd_args.ar_errors == GFS2_ERRORS_PANIC)
+ panic("GFS2: fsid=%s: panic requested.\n", sdp->sd_fsname);
return -1;
}
@@ -93,17 +99,24 @@ int gfs2_assert_warn_i(struct gfs2_sbd *sdp, char *assertion,
gfs2_tune_get(sdp, gt_complain_secs) * HZ))
return -2;
- printk(KERN_WARNING
- "GFS2: fsid=%s: warning: assertion \"%s\" failed\n"
- "GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
- sdp->sd_fsname, assertion,
- sdp->sd_fsname, function, file, line);
+ if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW)
+ printk(KERN_WARNING
+ "GFS2: fsid=%s: warning: assertion \"%s\" failed\n"
+ "GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
+ sdp->sd_fsname, assertion,
+ sdp->sd_fsname, function, file, line);
if (sdp->sd_args.ar_debug)
BUG();
else
dump_stack();
+ if (sdp->sd_args.ar_errors == GFS2_ERRORS_PANIC)
+ panic("GFS2: fsid=%s: warning: assertion \"%s\" failed\n"
+ "GFS2: fsid=%s: function = %s, file = %s, line = %u\n",
+ sdp->sd_fsname, assertion,
+ sdp->sd_fsname, function, file, line);
+
sdp->sd_last_warning = jiffies;
return -1;
diff --git a/fs/gfs2/eattr.c b/fs/gfs2/xattr.c
index 07ea9529add..8a0f8ef6ee2 100644
--- a/fs/gfs2/eattr.c
+++ b/fs/gfs2/xattr.c
@@ -18,8 +18,7 @@
#include "gfs2.h"
#include "incore.h"
#include "acl.h"
-#include "eaops.h"
-#include "eattr.h"
+#include "xattr.h"
#include "glock.h"
#include "inode.h"
#include "meta_io.h"
@@ -38,26 +37,32 @@
* Returns: 1 if the EA should be stuffed
*/
-static int ea_calc_size(struct gfs2_sbd *sdp, struct gfs2_ea_request *er,
+static int ea_calc_size(struct gfs2_sbd *sdp, unsigned int nsize, size_t dsize,
unsigned int *size)
{
- *size = GFS2_EAREQ_SIZE_STUFFED(er);
- if (*size <= sdp->sd_jbsize)
+ unsigned int jbsize = sdp->sd_jbsize;
+
+ /* Stuffed */
+ *size = ALIGN(sizeof(struct gfs2_ea_header) + nsize + dsize, 8);
+
+ if (*size <= jbsize)
return 1;
- *size = GFS2_EAREQ_SIZE_UNSTUFFED(sdp, er);
+ /* Unstuffed */
+ *size = ALIGN(sizeof(struct gfs2_ea_header) + nsize +
+ (sizeof(__be64) * DIV_ROUND_UP(dsize, jbsize)), 8);
return 0;
}
-static int ea_check_size(struct gfs2_sbd *sdp, struct gfs2_ea_request *er)
+static int ea_check_size(struct gfs2_sbd *sdp, unsigned int nsize, size_t dsize)
{
unsigned int size;
- if (er->er_data_len > GFS2_EA_MAX_DATA_LEN)
+ if (dsize > GFS2_EA_MAX_DATA_LEN)
return -ERANGE;
- ea_calc_size(sdp, er, &size);
+ ea_calc_size(sdp, nsize, dsize, &size);
/* This can only happen with 512 byte blocks */
if (size > sdp->sd_jbsize)
@@ -151,7 +156,9 @@ out:
}
struct ea_find {
- struct gfs2_ea_request *ef_er;
+ int type;
+ const char *name;
+ size_t namel;
struct gfs2_ea_location *ef_el;
};
@@ -160,14 +167,13 @@ static int ea_find_i(struct gfs2_inode *ip, struct buffer_head *bh,
void *private)
{
struct ea_find *ef = private;
- struct gfs2_ea_request *er = ef->ef_er;
if (ea->ea_type == GFS2_EATYPE_UNUSED)
return 0;
- if (ea->ea_type == er->er_type) {
- if (ea->ea_name_len == er->er_name_len &&
- !memcmp(GFS2_EA2NAME(ea), er->er_name, ea->ea_name_len)) {
+ if (ea->ea_type == ef->type) {
+ if (ea->ea_name_len == ef->namel &&
+ !memcmp(GFS2_EA2NAME(ea), ef->name, ea->ea_name_len)) {
struct gfs2_ea_location *el = ef->ef_el;
get_bh(bh);
el->el_bh = bh;
@@ -180,13 +186,15 @@ static int ea_find_i(struct gfs2_inode *ip, struct buffer_head *bh,
return 0;
}
-int gfs2_ea_find(struct gfs2_inode *ip, struct gfs2_ea_request *er,
+int gfs2_ea_find(struct gfs2_inode *ip, int type, const char *name,
struct gfs2_ea_location *el)
{
struct ea_find ef;
int error;
- ef.ef_er = er;
+ ef.type = type;
+ ef.name = name;
+ ef.namel = strlen(name);
ef.ef_el = el;
memset(el, 0, sizeof(struct gfs2_ea_location));
@@ -344,6 +352,20 @@ struct ea_list {
unsigned int ei_size;
};
+static inline unsigned int gfs2_ea_strlen(struct gfs2_ea_header *ea)
+{
+ switch (ea->ea_type) {
+ case GFS2_EATYPE_USR:
+ return 5 + ea->ea_name_len + 1;
+ case GFS2_EATYPE_SYS:
+ return 7 + ea->ea_name_len + 1;
+ case GFS2_EATYPE_SECURITY:
+ return 9 + ea->ea_name_len + 1;
+ default:
+ return 0;
+ }
+}
+
static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh,
struct gfs2_ea_header *ea, struct gfs2_ea_header *prev,
void *private)
@@ -392,21 +414,25 @@ static int ea_list_i(struct gfs2_inode *ip, struct buffer_head *bh,
}
/**
- * gfs2_ea_list -
- * @ip:
- * @er:
+ * gfs2_listxattr - List gfs2 extended attributes
+ * @dentry: The dentry whose inode we are interested in
+ * @buffer: The buffer to write the results
+ * @size: The size of the buffer
*
* Returns: actual size of data on success, -errno on error
*/
-int gfs2_ea_list(struct gfs2_inode *ip, struct gfs2_ea_request *er)
+ssize_t gfs2_listxattr(struct dentry *dentry, char *buffer, size_t size)
{
+ struct gfs2_inode *ip = GFS2_I(dentry->d_inode);
+ struct gfs2_ea_request er;
struct gfs2_holder i_gh;
int error;
- if (!er->er_data || !er->er_data_len) {
- er->er_data = NULL;
- er->er_data_len = 0;
+ memset(&er, 0, sizeof(struct gfs2_ea_request));
+ if (size) {
+ er.er_data = buffer;
+ er.er_data_len = size;
}
error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
@@ -414,7 +440,7 @@ int gfs2_ea_list(struct gfs2_inode *ip, struct gfs2_ea_request *er)
return error;
if (ip->i_eattr) {
- struct ea_list ei = { .ei_er = er, .ei_size = 0 };
+ struct ea_list ei = { .ei_er = &er, .ei_size = 0 };
error = ea_foreach(ip, ea_list_i, &ei);
if (!error)
@@ -491,84 +517,61 @@ out:
}
int gfs2_ea_get_copy(struct gfs2_inode *ip, struct gfs2_ea_location *el,
- char *data)
+ char *data, size_t size)
{
+ int ret;
+ size_t len = GFS2_EA_DATA_LEN(el->el_ea);
+ if (len > size)
+ return -ERANGE;
+
if (GFS2_EA_IS_STUFFED(el->el_ea)) {
- memcpy(data, GFS2_EA2DATA(el->el_ea), GFS2_EA_DATA_LEN(el->el_ea));
- return 0;
- } else
- return ea_get_unstuffed(ip, el->el_ea, data);
+ memcpy(data, GFS2_EA2DATA(el->el_ea), len);
+ return len;
+ }
+ ret = ea_get_unstuffed(ip, el->el_ea, data);
+ if (ret < 0)
+ return ret;
+ return len;
}
/**
- * gfs2_ea_get_i -
- * @ip: The GFS2 inode
- * @er: The request structure
+ * gfs2_xattr_get - Get a GFS2 extended attribute
+ * @inode: The inode
+ * @type: The type of extended attribute
+ * @name: The name of the extended attribute
+ * @buffer: The buffer to write the result into
+ * @size: The size of the buffer
*
* Returns: actual size of data on success, -errno on error
*/
-int gfs2_ea_get_i(struct gfs2_inode *ip, struct gfs2_ea_request *er)
+int gfs2_xattr_get(struct inode *inode, int type, const char *name,
+ void *buffer, size_t size)
{
+ struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_ea_location el;
int error;
if (!ip->i_eattr)
return -ENODATA;
+ if (strlen(name) > GFS2_EA_MAX_NAME_LEN)
+ return -EINVAL;
- error = gfs2_ea_find(ip, er, &el);
+ error = gfs2_ea_find(ip, type, name, &el);
if (error)
return error;
if (!el.el_ea)
return -ENODATA;
-
- if (er->er_data_len) {
- if (GFS2_EA_DATA_LEN(el.el_ea) > er->er_data_len)
- error = -ERANGE;
- else
- error = gfs2_ea_get_copy(ip, &el, er->er_data);
- }
- if (!error)
+ if (size)
+ error = gfs2_ea_get_copy(ip, &el, buffer, size);
+ else
error = GFS2_EA_DATA_LEN(el.el_ea);
-
brelse(el.el_bh);
return error;
}
/**
- * gfs2_ea_get -
- * @ip: The GFS2 inode
- * @er: The request structure
- *
- * Returns: actual size of data on success, -errno on error
- */
-
-int gfs2_ea_get(struct gfs2_inode *ip, struct gfs2_ea_request *er)
-{
- struct gfs2_holder i_gh;
- int error;
-
- if (!er->er_name_len ||
- er->er_name_len > GFS2_EA_MAX_NAME_LEN)
- return -EINVAL;
- if (!er->er_data || !er->er_data_len) {
- er->er_data = NULL;
- er->er_data_len = 0;
- }
-
- error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
- if (error)
- return error;
-
- error = gfs2_ea_ops[er->er_type]->eo_get(ip, er);
-
- gfs2_glock_dq_uninit(&i_gh);
-
- return error;
-}
-
-/**
* ea_alloc_blk - allocates a new block for extended attributes.
* @ip: A pointer to the inode that's getting extended attributes
* @bhp: Pointer to pointer to a struct buffer_head
@@ -713,12 +716,6 @@ static int ea_alloc_skeleton(struct gfs2_inode *ip, struct gfs2_ea_request *er,
error = gfs2_meta_inode_buffer(ip, &dibh);
if (!error) {
- if (er->er_flags & GFS2_ERF_MODE) {
- gfs2_assert_withdraw(GFS2_SB(&ip->i_inode),
- (ip->i_inode.i_mode & S_IFMT) ==
- (er->er_mode & S_IFMT));
- ip->i_inode.i_mode = er->er_mode;
- }
ip->i_inode.i_ctime = CURRENT_TIME;
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
gfs2_dinode_out(ip, dibh->b_data);
@@ -762,15 +759,23 @@ static int ea_init_i(struct gfs2_inode *ip, struct gfs2_ea_request *er,
* Returns: errno
*/
-static int ea_init(struct gfs2_inode *ip, struct gfs2_ea_request *er)
+static int ea_init(struct gfs2_inode *ip, int type, const char *name,
+ const void *data, size_t size)
{
+ struct gfs2_ea_request er;
unsigned int jbsize = GFS2_SB(&ip->i_inode)->sd_jbsize;
unsigned int blks = 1;
- if (GFS2_EAREQ_SIZE_STUFFED(er) > jbsize)
- blks += DIV_ROUND_UP(er->er_data_len, jbsize);
+ er.er_type = type;
+ er.er_name = name;
+ er.er_name_len = strlen(name);
+ er.er_data = (void *)data;
+ er.er_data_len = size;
+
+ if (GFS2_EAREQ_SIZE_STUFFED(&er) > jbsize)
+ blks += DIV_ROUND_UP(er.er_data_len, jbsize);
- return ea_alloc_skeleton(ip, er, blks, ea_init_i, NULL);
+ return ea_alloc_skeleton(ip, &er, blks, ea_init_i, NULL);
}
static struct gfs2_ea_header *ea_split_ea(struct gfs2_ea_header *ea)
@@ -848,12 +853,6 @@ static int ea_set_simple_noalloc(struct gfs2_inode *ip, struct buffer_head *bh,
error = gfs2_meta_inode_buffer(ip, &dibh);
if (error)
goto out;
-
- if (er->er_flags & GFS2_ERF_MODE) {
- gfs2_assert_withdraw(GFS2_SB(&ip->i_inode),
- (ip->i_inode.i_mode & S_IFMT) == (er->er_mode & S_IFMT));
- ip->i_inode.i_mode = er->er_mode;
- }
ip->i_inode.i_ctime = CURRENT_TIME;
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
gfs2_dinode_out(ip, dibh->b_data);
@@ -894,7 +893,8 @@ static int ea_set_simple(struct gfs2_inode *ip, struct buffer_head *bh,
int stuffed;
int error;
- stuffed = ea_calc_size(GFS2_SB(&ip->i_inode), es->es_er, &size);
+ stuffed = ea_calc_size(GFS2_SB(&ip->i_inode), es->es_er->er_name_len,
+ es->es_er->er_data_len, &size);
if (ea->ea_type == GFS2_EATYPE_UNUSED) {
if (GFS2_EA_REC_LEN(ea) < size)
@@ -1005,15 +1005,22 @@ out:
return error;
}
-static int ea_set_i(struct gfs2_inode *ip, struct gfs2_ea_request *er,
- struct gfs2_ea_location *el)
+static int ea_set_i(struct gfs2_inode *ip, int type, const char *name,
+ const void *value, size_t size, struct gfs2_ea_location *el)
{
+ struct gfs2_ea_request er;
struct ea_set es;
unsigned int blks = 2;
int error;
+ er.er_type = type;
+ er.er_name = name;
+ er.er_data = (void *)value;
+ er.er_name_len = strlen(name);
+ er.er_data_len = size;
+
memset(&es, 0, sizeof(struct ea_set));
- es.es_er = er;
+ es.es_er = &er;
es.es_el = el;
error = ea_foreach(ip, ea_set_simple, &es);
@@ -1024,10 +1031,10 @@ static int ea_set_i(struct gfs2_inode *ip, struct gfs2_ea_request *er,
if (!(ip->i_diskflags & GFS2_DIF_EA_INDIRECT))
blks++;
- if (GFS2_EAREQ_SIZE_STUFFED(er) > GFS2_SB(&ip->i_inode)->sd_jbsize)
- blks += DIV_ROUND_UP(er->er_data_len, GFS2_SB(&ip->i_inode)->sd_jbsize);
+ if (GFS2_EAREQ_SIZE_STUFFED(&er) > GFS2_SB(&ip->i_inode)->sd_jbsize)
+ blks += DIV_ROUND_UP(er.er_data_len, GFS2_SB(&ip->i_inode)->sd_jbsize);
- return ea_alloc_skeleton(ip, er, blks, ea_set_block, el);
+ return ea_alloc_skeleton(ip, &er, blks, ea_set_block, el);
}
static int ea_set_remove_unstuffed(struct gfs2_inode *ip,
@@ -1039,75 +1046,7 @@ static int ea_set_remove_unstuffed(struct gfs2_inode *ip,
GFS2_EA2NEXT(el->el_prev) == el->el_ea);
}
- return ea_remove_unstuffed(ip, el->el_bh, el->el_ea, el->el_prev,0);
-}
-
-int gfs2_ea_set_i(struct gfs2_inode *ip, struct gfs2_ea_request *er)
-{
- struct gfs2_ea_location el;
- int error;
-
- if (!ip->i_eattr) {
- if (er->er_flags & XATTR_REPLACE)
- return -ENODATA;
- return ea_init(ip, er);
- }
-
- error = gfs2_ea_find(ip, er, &el);
- if (error)
- return error;
-
- if (el.el_ea) {
- if (ip->i_diskflags & GFS2_DIF_APPENDONLY) {
- brelse(el.el_bh);
- return -EPERM;
- }
-
- error = -EEXIST;
- if (!(er->er_flags & XATTR_CREATE)) {
- int unstuffed = !GFS2_EA_IS_STUFFED(el.el_ea);
- error = ea_set_i(ip, er, &el);
- if (!error && unstuffed)
- ea_set_remove_unstuffed(ip, &el);
- }
-
- brelse(el.el_bh);
- } else {
- error = -ENODATA;
- if (!(er->er_flags & XATTR_REPLACE))
- error = ea_set_i(ip, er, NULL);
- }
-
- return error;
-}
-
-int gfs2_ea_set(struct gfs2_inode *ip, struct gfs2_ea_request *er)
-{
- struct gfs2_holder i_gh;
- int error;
-
- if (!er->er_name_len || er->er_name_len > GFS2_EA_MAX_NAME_LEN)
- return -EINVAL;
- if (!er->er_data || !er->er_data_len) {
- er->er_data = NULL;
- er->er_data_len = 0;
- }
- error = ea_check_size(GFS2_SB(&ip->i_inode), er);
- if (error)
- return error;
-
- error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
- if (error)
- return error;
-
- if (IS_IMMUTABLE(&ip->i_inode))
- error = -EPERM;
- else
- error = gfs2_ea_ops[er->er_type]->eo_set(ip, er);
-
- gfs2_glock_dq_uninit(&i_gh);
-
- return error;
+ return ea_remove_unstuffed(ip, el->el_bh, el->el_ea, el->el_prev, 0);
}
static int ea_remove_stuffed(struct gfs2_inode *ip, struct gfs2_ea_location *el)
@@ -1131,8 +1070,9 @@ static int ea_remove_stuffed(struct gfs2_inode *ip, struct gfs2_ea_location *el)
if (GFS2_EA_IS_LAST(ea))
prev->ea_flags |= GFS2_EAFLAG_LAST;
- } else
+ } else {
ea->ea_type = GFS2_EATYPE_UNUSED;
+ }
error = gfs2_meta_inode_buffer(ip, &dibh);
if (!error) {
@@ -1147,15 +1087,29 @@ static int ea_remove_stuffed(struct gfs2_inode *ip, struct gfs2_ea_location *el)
return error;
}
-int gfs2_ea_remove_i(struct gfs2_inode *ip, struct gfs2_ea_request *er)
+/**
+ * gfs2_xattr_remove - Remove a GFS2 extended attribute
+ * @inode: The inode
+ * @type: The type of the extended attribute
+ * @name: The name of the extended attribute
+ *
+ * This is not called directly by the VFS since we use the (common)
+ * scheme of making a "set with NULL data" mean a remove request. Note
+ * that this is different from a set with zero length data.
+ *
+ * Returns: 0, or errno on failure
+ */
+
+static int gfs2_xattr_remove(struct inode *inode, int type, const char *name)
{
+ struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_ea_location el;
int error;
if (!ip->i_eattr)
return -ENODATA;
- error = gfs2_ea_find(ip, er, &el);
+ error = gfs2_ea_find(ip, type, name, &el);
if (error)
return error;
if (!el.el_ea)
@@ -1164,8 +1118,7 @@ int gfs2_ea_remove_i(struct gfs2_inode *ip, struct gfs2_ea_request *er)
if (GFS2_EA_IS_STUFFED(el.el_ea))
error = ea_remove_stuffed(ip, &el);
else
- error = ea_remove_unstuffed(ip, el.el_bh, el.el_ea, el.el_prev,
- 0);
+ error = ea_remove_unstuffed(ip, el.el_bh, el.el_ea, el.el_prev, 0);
brelse(el.el_bh);
@@ -1173,31 +1126,70 @@ int gfs2_ea_remove_i(struct gfs2_inode *ip, struct gfs2_ea_request *er)
}
/**
- * gfs2_ea_remove - sets (or creates or replaces) an extended attribute
- * @ip: pointer to the inode of the target file
- * @er: request information
+ * gfs2_xattr_set - Set (or remove) a GFS2 extended attribute
+ * @inode: The inode
+ * @type: The type of the extended attribute
+ * @name: The name of the extended attribute
+ * @value: The value of the extended attribute (NULL for remove)
+ * @size: The size of the @value argument
+ * @flags: Create or Replace
*
- * Returns: errno
+ * See gfs2_xattr_remove() for details of the removal of xattrs.
+ *
+ * Returns: 0 or errno on failure
*/
-int gfs2_ea_remove(struct gfs2_inode *ip, struct gfs2_ea_request *er)
+int gfs2_xattr_set(struct inode *inode, int type, const char *name,
+ const void *value, size_t size, int flags)
{
- struct gfs2_holder i_gh;
+ struct gfs2_sbd *sdp = GFS2_SB(inode);
+ struct gfs2_inode *ip = GFS2_I(inode);
+ struct gfs2_ea_location el;
+ unsigned int namel = strlen(name);
int error;
- if (!er->er_name_len || er->er_name_len > GFS2_EA_MAX_NAME_LEN)
- return -EINVAL;
+ if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+ return -EPERM;
+ if (namel > GFS2_EA_MAX_NAME_LEN)
+ return -ERANGE;
- error = gfs2_glock_nq_init(ip->i_gl, LM_ST_EXCLUSIVE, 0, &i_gh);
+ if (value == NULL)
+ return gfs2_xattr_remove(inode, type, name);
+
+ if (ea_check_size(sdp, namel, size))
+ return -ERANGE;
+
+ if (!ip->i_eattr) {
+ if (flags & XATTR_REPLACE)
+ return -ENODATA;
+ return ea_init(ip, type, name, value, size);
+ }
+
+ error = gfs2_ea_find(ip, type, name, &el);
if (error)
return error;
- if (IS_IMMUTABLE(&ip->i_inode) || IS_APPEND(&ip->i_inode))
- error = -EPERM;
- else
- error = gfs2_ea_ops[er->er_type]->eo_remove(ip, er);
+ if (el.el_ea) {
+ if (ip->i_diskflags & GFS2_DIF_APPENDONLY) {
+ brelse(el.el_bh);
+ return -EPERM;
+ }
- gfs2_glock_dq_uninit(&i_gh);
+ error = -EEXIST;
+ if (!(flags & XATTR_CREATE)) {
+ int unstuffed = !GFS2_EA_IS_STUFFED(el.el_ea);
+ error = ea_set_i(ip, type, name, value, size, &el);
+ if (!error && unstuffed)
+ ea_set_remove_unstuffed(ip, &el);
+ }
+
+ brelse(el.el_bh);
+ return error;
+ }
+
+ error = -ENODATA;
+ if (!(flags & XATTR_REPLACE))
+ error = ea_set_i(ip, type, name, value, size, NULL);
return error;
}
@@ -1503,3 +1495,64 @@ out_alloc:
return error;
}
+static int gfs2_xattr_user_get(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ return gfs2_xattr_get(inode, GFS2_EATYPE_USR, name, buffer, size);
+}
+
+static int gfs2_xattr_user_set(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ return gfs2_xattr_set(inode, GFS2_EATYPE_USR, name, value, size, flags);
+}
+
+static int gfs2_xattr_system_get(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ return gfs2_xattr_get(inode, GFS2_EATYPE_SYS, name, buffer, size);
+}
+
+static int gfs2_xattr_system_set(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ return gfs2_xattr_set(inode, GFS2_EATYPE_SYS, name, value, size, flags);
+}
+
+static int gfs2_xattr_security_get(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ return gfs2_xattr_get(inode, GFS2_EATYPE_SECURITY, name, buffer, size);
+}
+
+static int gfs2_xattr_security_set(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ return gfs2_xattr_set(inode, GFS2_EATYPE_SECURITY, name, value, size, flags);
+}
+
+static struct xattr_handler gfs2_xattr_user_handler = {
+ .prefix = XATTR_USER_PREFIX,
+ .get = gfs2_xattr_user_get,
+ .set = gfs2_xattr_user_set,
+};
+
+static struct xattr_handler gfs2_xattr_security_handler = {
+ .prefix = XATTR_SECURITY_PREFIX,
+ .get = gfs2_xattr_security_get,
+ .set = gfs2_xattr_security_set,
+};
+
+static struct xattr_handler gfs2_xattr_system_handler = {
+ .prefix = XATTR_SYSTEM_PREFIX,
+ .get = gfs2_xattr_system_get,
+ .set = gfs2_xattr_system_set,
+};
+
+struct xattr_handler *gfs2_xattr_handlers[] = {
+ &gfs2_xattr_user_handler,
+ &gfs2_xattr_security_handler,
+ &gfs2_xattr_system_handler,
+ NULL,
+};
+
diff --git a/fs/gfs2/eattr.h b/fs/gfs2/xattr.h
index c82dbe01d71..cbdfd774373 100644
--- a/fs/gfs2/eattr.h
+++ b/fs/gfs2/xattr.h
@@ -19,7 +19,7 @@ struct iattr;
#define GFS2_EA_SIZE(ea) \
ALIGN(sizeof(struct gfs2_ea_header) + (ea)->ea_name_len + \
((GFS2_EA_IS_STUFFED(ea)) ? GFS2_EA_DATA_LEN(ea) : \
- (sizeof(__be64) * (ea)->ea_num_ptrs)), 8)
+ (sizeof(__be64) * (ea)->ea_num_ptrs)), 8)
#define GFS2_EA_IS_STUFFED(ea) (!(ea)->ea_num_ptrs)
#define GFS2_EA_IS_LAST(ea) ((ea)->ea_flags & GFS2_EAFLAG_LAST)
@@ -27,10 +27,6 @@ ALIGN(sizeof(struct gfs2_ea_header) + (ea)->ea_name_len + \
#define GFS2_EAREQ_SIZE_STUFFED(er) \
ALIGN(sizeof(struct gfs2_ea_header) + (er)->er_name_len + (er)->er_data_len, 8)
-#define GFS2_EAREQ_SIZE_UNSTUFFED(sdp, er) \
-ALIGN(sizeof(struct gfs2_ea_header) + (er)->er_name_len + \
- sizeof(__be64) * DIV_ROUND_UP((er)->er_data_len, (sdp)->sd_jbsize), 8)
-
#define GFS2_EA2NAME(ea) ((char *)((struct gfs2_ea_header *)(ea) + 1))
#define GFS2_EA2DATA(ea) (GFS2_EA2NAME(ea) + (ea)->ea_name_len)
@@ -43,16 +39,12 @@ ALIGN(sizeof(struct gfs2_ea_header) + (er)->er_name_len + \
#define GFS2_EA_BH2FIRST(bh) \
((struct gfs2_ea_header *)((bh)->b_data + sizeof(struct gfs2_meta_header)))
-#define GFS2_ERF_MODE 0x80000000
-
struct gfs2_ea_request {
const char *er_name;
char *er_data;
unsigned int er_name_len;
unsigned int er_data_len;
unsigned int er_type; /* GFS2_EATYPE_... */
- int er_flags;
- mode_t er_mode;
};
struct gfs2_ea_location {
@@ -61,40 +53,20 @@ struct gfs2_ea_location {
struct gfs2_ea_header *el_prev;
};
-int gfs2_ea_get_i(struct gfs2_inode *ip, struct gfs2_ea_request *er);
-int gfs2_ea_set_i(struct gfs2_inode *ip, struct gfs2_ea_request *er);
-int gfs2_ea_remove_i(struct gfs2_inode *ip, struct gfs2_ea_request *er);
-
-int gfs2_ea_list(struct gfs2_inode *ip, struct gfs2_ea_request *er);
-int gfs2_ea_get(struct gfs2_inode *ip, struct gfs2_ea_request *er);
-int gfs2_ea_set(struct gfs2_inode *ip, struct gfs2_ea_request *er);
-int gfs2_ea_remove(struct gfs2_inode *ip, struct gfs2_ea_request *er);
-
-int gfs2_ea_dealloc(struct gfs2_inode *ip);
+extern int gfs2_xattr_get(struct inode *inode, int type, const char *name,
+ void *buffer, size_t size);
+extern int gfs2_xattr_set(struct inode *inode, int type, const char *name,
+ const void *value, size_t size, int flags);
+extern ssize_t gfs2_listxattr(struct dentry *dentry, char *buffer, size_t size);
+extern int gfs2_ea_dealloc(struct gfs2_inode *ip);
/* Exported to acl.c */
-int gfs2_ea_find(struct gfs2_inode *ip,
- struct gfs2_ea_request *er,
- struct gfs2_ea_location *el);
-int gfs2_ea_get_copy(struct gfs2_inode *ip,
- struct gfs2_ea_location *el,
- char *data);
-int gfs2_ea_acl_chmod(struct gfs2_inode *ip, struct gfs2_ea_location *el,
- struct iattr *attr, char *data);
-
-static inline unsigned int gfs2_ea_strlen(struct gfs2_ea_header *ea)
-{
- switch (ea->ea_type) {
- case GFS2_EATYPE_USR:
- return 5 + ea->ea_name_len + 1;
- case GFS2_EATYPE_SYS:
- return 7 + ea->ea_name_len + 1;
- case GFS2_EATYPE_SECURITY:
- return 9 + ea->ea_name_len + 1;
- default:
- return 0;
- }
-}
+extern int gfs2_ea_find(struct gfs2_inode *ip, int type, const char *name,
+ struct gfs2_ea_location *el);
+extern int gfs2_ea_get_copy(struct gfs2_inode *ip, struct gfs2_ea_location *el,
+ char *data, size_t size);
+extern int gfs2_ea_acl_chmod(struct gfs2_inode *ip, struct gfs2_ea_location *el,
+ struct iattr *attr, char *data);
#endif /* __EATTR_DOT_H__ */
diff --git a/fs/nilfs2/Kconfig b/fs/nilfs2/Kconfig
index 72da095d400..251da07b2a1 100644
--- a/fs/nilfs2/Kconfig
+++ b/fs/nilfs2/Kconfig
@@ -1,6 +1,6 @@
config NILFS2_FS
tristate "NILFS2 file system support (EXPERIMENTAL)"
- depends on BLOCK && EXPERIMENTAL
+ depends on EXPERIMENTAL
select CRC32
help
NILFS2 is a log-structured file system (LFS) supporting continuous
diff --git a/fs/nilfs2/bmap.c b/fs/nilfs2/bmap.c
index 99d58a028b9..08834df6ec6 100644
--- a/fs/nilfs2/bmap.c
+++ b/fs/nilfs2/bmap.c
@@ -36,6 +36,26 @@ struct inode *nilfs_bmap_get_dat(const struct nilfs_bmap *bmap)
return nilfs_dat_inode(NILFS_I_NILFS(bmap->b_inode));
}
+/**
+ * nilfs_bmap_lookup_at_level - find a data block or node block
+ * @bmap: bmap
+ * @key: key
+ * @level: level
+ * @ptrp: place to store the value associated to @key
+ *
+ * Description: nilfs_bmap_lookup_at_level() finds a record whose key
+ * matches @key in the block at @level of the bmap.
+ *
+ * Return Value: On success, 0 is returned and the record associated with @key
+ * is stored in the place pointed by @ptrp. On error, one of the following
+ * negative error codes is returned.
+ *
+ * %-EIO - I/O error.
+ *
+ * %-ENOMEM - Insufficient amount of memory available.
+ *
+ * %-ENOENT - A record associated with @key does not exist.
+ */
int nilfs_bmap_lookup_at_level(struct nilfs_bmap *bmap, __u64 key, int level,
__u64 *ptrp)
{
@@ -69,39 +89,6 @@ int nilfs_bmap_lookup_contig(struct nilfs_bmap *bmap, __u64 key, __u64 *ptrp,
return ret;
}
-/**
- * nilfs_bmap_lookup - find a record
- * @bmap: bmap
- * @key: key
- * @recp: pointer to record
- *
- * Description: nilfs_bmap_lookup() finds a record whose key matches @key in
- * @bmap.
- *
- * Return Value: On success, 0 is returned and the record associated with @key
- * is stored in the place pointed by @recp. On error, one of the following
- * negative error codes is returned.
- *
- * %-EIO - I/O error.
- *
- * %-ENOMEM - Insufficient amount of memory available.
- *
- * %-ENOENT - A record associated with @key does not exist.
- */
-int nilfs_bmap_lookup(struct nilfs_bmap *bmap,
- unsigned long key,
- unsigned long *recp)
-{
- __u64 ptr;
- int ret;
-
- /* XXX: use macro for level 1 */
- ret = nilfs_bmap_lookup_at_level(bmap, key, 1, &ptr);
- if (recp != NULL)
- *recp = ptr;
- return ret;
-}
-
static int nilfs_bmap_do_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr)
{
__u64 keys[NILFS_BMAP_SMALL_HIGH + 1];
@@ -469,104 +456,6 @@ __u64 nilfs_bmap_find_target_in_group(const struct nilfs_bmap *bmap)
(entries_per_group / NILFS_BMAP_GROUP_DIV);
}
-int nilfs_bmap_prepare_alloc_v(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *req)
-{
- return nilfs_dat_prepare_alloc(nilfs_bmap_get_dat(bmap), &req->bpr_req);
-}
-
-void nilfs_bmap_commit_alloc_v(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *req)
-{
- nilfs_dat_commit_alloc(nilfs_bmap_get_dat(bmap), &req->bpr_req);
-}
-
-void nilfs_bmap_abort_alloc_v(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *req)
-{
- nilfs_dat_abort_alloc(nilfs_bmap_get_dat(bmap), &req->bpr_req);
-}
-
-int nilfs_bmap_start_v(struct nilfs_bmap *bmap, union nilfs_bmap_ptr_req *req,
- sector_t blocknr)
-{
- struct inode *dat = nilfs_bmap_get_dat(bmap);
- int ret;
-
- ret = nilfs_dat_prepare_start(dat, &req->bpr_req);
- if (likely(!ret))
- nilfs_dat_commit_start(dat, &req->bpr_req, blocknr);
- return ret;
-}
-
-int nilfs_bmap_prepare_end_v(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *req)
-{
- return nilfs_dat_prepare_end(nilfs_bmap_get_dat(bmap), &req->bpr_req);
-}
-
-void nilfs_bmap_commit_end_v(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *req)
-{
- nilfs_dat_commit_end(nilfs_bmap_get_dat(bmap), &req->bpr_req,
- bmap->b_ptr_type == NILFS_BMAP_PTR_VS);
-}
-
-void nilfs_bmap_abort_end_v(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *req)
-{
- nilfs_dat_abort_end(nilfs_bmap_get_dat(bmap), &req->bpr_req);
-}
-
-int nilfs_bmap_move_v(const struct nilfs_bmap *bmap, __u64 vblocknr,
- sector_t blocknr)
-{
- return nilfs_dat_move(nilfs_bmap_get_dat(bmap), vblocknr, blocknr);
-}
-
-int nilfs_bmap_mark_dirty(const struct nilfs_bmap *bmap, __u64 vblocknr)
-{
- return nilfs_dat_mark_dirty(nilfs_bmap_get_dat(bmap), vblocknr);
-}
-
-int nilfs_bmap_prepare_update_v(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *oldreq,
- union nilfs_bmap_ptr_req *newreq)
-{
- struct inode *dat = nilfs_bmap_get_dat(bmap);
- int ret;
-
- ret = nilfs_dat_prepare_end(dat, &oldreq->bpr_req);
- if (ret < 0)
- return ret;
- ret = nilfs_dat_prepare_alloc(dat, &newreq->bpr_req);
- if (ret < 0)
- nilfs_dat_abort_end(dat, &oldreq->bpr_req);
-
- return ret;
-}
-
-void nilfs_bmap_commit_update_v(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *oldreq,
- union nilfs_bmap_ptr_req *newreq)
-{
- struct inode *dat = nilfs_bmap_get_dat(bmap);
-
- nilfs_dat_commit_end(dat, &oldreq->bpr_req,
- bmap->b_ptr_type == NILFS_BMAP_PTR_VS);
- nilfs_dat_commit_alloc(dat, &newreq->bpr_req);
-}
-
-void nilfs_bmap_abort_update_v(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *oldreq,
- union nilfs_bmap_ptr_req *newreq)
-{
- struct inode *dat = nilfs_bmap_get_dat(bmap);
-
- nilfs_dat_abort_end(dat, &oldreq->bpr_req);
- nilfs_dat_abort_alloc(dat, &newreq->bpr_req);
-}
-
static struct lock_class_key nilfs_bmap_dat_lock_key;
static struct lock_class_key nilfs_bmap_mdt_lock_key;
diff --git a/fs/nilfs2/bmap.h b/fs/nilfs2/bmap.h
index b2890cdcef1..9980d7dbab9 100644
--- a/fs/nilfs2/bmap.h
+++ b/fs/nilfs2/bmap.h
@@ -28,6 +28,7 @@
#include <linux/buffer_head.h>
#include <linux/nilfs2_fs.h>
#include "alloc.h"
+#include "dat.h"
#define NILFS_BMAP_INVALID_PTR 0
@@ -141,7 +142,6 @@ struct nilfs_bmap {
int nilfs_bmap_test_and_clear_dirty(struct nilfs_bmap *);
int nilfs_bmap_read(struct nilfs_bmap *, struct nilfs_inode *);
void nilfs_bmap_write(struct nilfs_bmap *, struct nilfs_inode *);
-int nilfs_bmap_lookup(struct nilfs_bmap *, unsigned long, unsigned long *);
int nilfs_bmap_lookup_contig(struct nilfs_bmap *, __u64, __u64 *, unsigned);
int nilfs_bmap_insert(struct nilfs_bmap *, unsigned long, unsigned long);
int nilfs_bmap_delete(struct nilfs_bmap *, unsigned long);
@@ -160,90 +160,76 @@ void nilfs_bmap_init_gcdat(struct nilfs_bmap *, struct nilfs_bmap *);
void nilfs_bmap_commit_gcdat(struct nilfs_bmap *, struct nilfs_bmap *);
+static inline int nilfs_bmap_lookup(struct nilfs_bmap *bmap, __u64 key,
+ __u64 *ptr)
+{
+ return nilfs_bmap_lookup_at_level(bmap, key, 1, ptr);
+}
+
/*
* Internal use only
*/
struct inode *nilfs_bmap_get_dat(const struct nilfs_bmap *);
-int nilfs_bmap_prepare_alloc_v(struct nilfs_bmap *,
- union nilfs_bmap_ptr_req *);
-void nilfs_bmap_commit_alloc_v(struct nilfs_bmap *,
- union nilfs_bmap_ptr_req *);
-void nilfs_bmap_abort_alloc_v(struct nilfs_bmap *,
- union nilfs_bmap_ptr_req *);
static inline int nilfs_bmap_prepare_alloc_ptr(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *req)
+ union nilfs_bmap_ptr_req *req,
+ struct inode *dat)
{
- if (NILFS_BMAP_USE_VBN(bmap))
- return nilfs_bmap_prepare_alloc_v(bmap, req);
+ if (dat)
+ return nilfs_dat_prepare_alloc(dat, &req->bpr_req);
/* ignore target ptr */
req->bpr_ptr = bmap->b_last_allocated_ptr++;
return 0;
}
static inline void nilfs_bmap_commit_alloc_ptr(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *req)
+ union nilfs_bmap_ptr_req *req,
+ struct inode *dat)
{
- if (NILFS_BMAP_USE_VBN(bmap))
- nilfs_bmap_commit_alloc_v(bmap, req);
+ if (dat)
+ nilfs_dat_commit_alloc(dat, &req->bpr_req);
}
static inline void nilfs_bmap_abort_alloc_ptr(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *req)
+ union nilfs_bmap_ptr_req *req,
+ struct inode *dat)
{
- if (NILFS_BMAP_USE_VBN(bmap))
- nilfs_bmap_abort_alloc_v(bmap, req);
+ if (dat)
+ nilfs_dat_abort_alloc(dat, &req->bpr_req);
else
bmap->b_last_allocated_ptr--;
}
-int nilfs_bmap_prepare_end_v(struct nilfs_bmap *, union nilfs_bmap_ptr_req *);
-void nilfs_bmap_commit_end_v(struct nilfs_bmap *, union nilfs_bmap_ptr_req *);
-void nilfs_bmap_abort_end_v(struct nilfs_bmap *, union nilfs_bmap_ptr_req *);
-
static inline int nilfs_bmap_prepare_end_ptr(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *req)
+ union nilfs_bmap_ptr_req *req,
+ struct inode *dat)
{
- return NILFS_BMAP_USE_VBN(bmap) ?
- nilfs_bmap_prepare_end_v(bmap, req) : 0;
+ return dat ? nilfs_dat_prepare_end(dat, &req->bpr_req) : 0;
}
static inline void nilfs_bmap_commit_end_ptr(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *req)
+ union nilfs_bmap_ptr_req *req,
+ struct inode *dat)
{
- if (NILFS_BMAP_USE_VBN(bmap))
- nilfs_bmap_commit_end_v(bmap, req);
+ if (dat)
+ nilfs_dat_commit_end(dat, &req->bpr_req,
+ bmap->b_ptr_type == NILFS_BMAP_PTR_VS);
}
static inline void nilfs_bmap_abort_end_ptr(struct nilfs_bmap *bmap,
- union nilfs_bmap_ptr_req *req)
+ union nilfs_bmap_ptr_req *req,
+ struct inode *dat)
{
- if (NILFS_BMAP_USE_VBN(bmap))
- nilfs_bmap_abort_end_v(bmap, req);
+ if (dat)
+ nilfs_dat_abort_end(dat, &req->bpr_req);
}
-int nilfs_bmap_start_v(struct nilfs_bmap *, union nilfs_bmap_ptr_req *,
- sector_t);
-int nilfs_bmap_move_v(const struct nilfs_bmap *, __u64, sector_t);
-int nilfs_bmap_mark_dirty(const struct nilfs_bmap *, __u64);
-
-
__u64 nilfs_bmap_data_get_key(const struct nilfs_bmap *,
const struct buffer_head *);
__u64 nilfs_bmap_find_target_seq(const struct nilfs_bmap *, __u64);
__u64 nilfs_bmap_find_target_in_group(const struct nilfs_bmap *);
-int nilfs_bmap_prepare_update_v(struct nilfs_bmap *,
- union nilfs_bmap_ptr_req *,
- union nilfs_bmap_ptr_req *);
-void nilfs_bmap_commit_update_v(struct nilfs_bmap *,
- union nilfs_bmap_ptr_req *,
- union nilfs_bmap_ptr_req *);
-void nilfs_bmap_abort_update_v(struct nilfs_bmap *,
- union nilfs_bmap_ptr_req *,
- union nilfs_bmap_ptr_req *);
-
void nilfs_bmap_add_blocks(const struct nilfs_bmap *, int);
void nilfs_bmap_sub_blocks(const struct nilfs_bmap *, int);
diff --git a/fs/nilfs2/btree.c b/fs/nilfs2/btree.c
index aa412724b64..e25b507a474 100644
--- a/fs/nilfs2/btree.c
+++ b/fs/nilfs2/btree.c
@@ -71,21 +71,17 @@ void nilfs_btree_path_cache_destroy(void)
kmem_cache_destroy(nilfs_btree_path_cache);
}
-static inline struct nilfs_btree_path *
-nilfs_btree_alloc_path(const struct nilfs_btree *btree)
+static inline struct nilfs_btree_path *nilfs_btree_alloc_path(void)
{
- return (struct nilfs_btree_path *)
- kmem_cache_alloc(nilfs_btree_path_cache, GFP_NOFS);
+ return kmem_cache_alloc(nilfs_btree_path_cache, GFP_NOFS);
}
-static inline void nilfs_btree_free_path(const struct nilfs_btree *btree,
- struct nilfs_btree_path *path)
+static inline void nilfs_btree_free_path(struct nilfs_btree_path *path)
{
kmem_cache_free(nilfs_btree_path_cache, path);
}
-static void nilfs_btree_init_path(const struct nilfs_btree *btree,
- struct nilfs_btree_path *path)
+static void nilfs_btree_init_path(struct nilfs_btree_path *path)
{
int level;
@@ -101,26 +97,13 @@ static void nilfs_btree_init_path(const struct nilfs_btree *btree,
}
}
-static void nilfs_btree_clear_path(const struct nilfs_btree *btree,
- struct nilfs_btree_path *path)
+static void nilfs_btree_release_path(struct nilfs_btree_path *path)
{
int level;
- for (level = NILFS_BTREE_LEVEL_DATA;
- level < NILFS_BTREE_LEVEL_MAX;
- level++) {
- if (path[level].bp_bh != NULL) {
- brelse(path[level].bp_bh);
- path[level].bp_bh = NULL;
- }
- /* sib_bh is released or deleted by prepare or commit
- * operations. */
- path[level].bp_sib_bh = NULL;
- path[level].bp_index = 0;
- path[level].bp_oldreq.bpr_ptr = NILFS_BMAP_INVALID_PTR;
- path[level].bp_newreq.bpr_ptr = NILFS_BMAP_INVALID_PTR;
- path[level].bp_op = NULL;
- }
+ for (level = NILFS_BTREE_LEVEL_DATA; level < NILFS_BTREE_LEVEL_MAX;
+ level++)
+ brelse(path[level].bp_bh);
}
/*
@@ -148,129 +131,110 @@ static int nilfs_btree_get_new_block(const struct nilfs_btree *btree,
}
static inline int
-nilfs_btree_node_get_flags(const struct nilfs_btree *btree,
- const struct nilfs_btree_node *node)
+nilfs_btree_node_get_flags(const struct nilfs_btree_node *node)
{
return node->bn_flags;
}
static inline void
-nilfs_btree_node_set_flags(struct nilfs_btree *btree,
- struct nilfs_btree_node *node,
- int flags)
+nilfs_btree_node_set_flags(struct nilfs_btree_node *node, int flags)
{
node->bn_flags = flags;
}
-static inline int nilfs_btree_node_root(const struct nilfs_btree *btree,
- const struct nilfs_btree_node *node)
+static inline int nilfs_btree_node_root(const struct nilfs_btree_node *node)
{
- return nilfs_btree_node_get_flags(btree, node) & NILFS_BTREE_NODE_ROOT;
+ return nilfs_btree_node_get_flags(node) & NILFS_BTREE_NODE_ROOT;
}
static inline int
-nilfs_btree_node_get_level(const struct nilfs_btree *btree,
- const struct nilfs_btree_node *node)
+nilfs_btree_node_get_level(const struct nilfs_btree_node *node)
{
return node->bn_level;
}
static inline void
-nilfs_btree_node_set_level(struct nilfs_btree *btree,
- struct nilfs_btree_node *node,
- int level)
+nilfs_btree_node_set_level(struct nilfs_btree_node *node, int level)
{
node->bn_level = level;
}
static inline int
-nilfs_btree_node_get_nchildren(const struct nilfs_btree *btree,
- const struct nilfs_btree_node *node)
+nilfs_btree_node_get_nchildren(const struct nilfs_btree_node *node)
{
return le16_to_cpu(node->bn_nchildren);
}
static inline void
-nilfs_btree_node_set_nchildren(struct nilfs_btree *btree,
- struct nilfs_btree_node *node,
- int nchildren)
+nilfs_btree_node_set_nchildren(struct nilfs_btree_node *node, int nchildren)
{
node->bn_nchildren = cpu_to_le16(nchildren);
}
-static inline int
-nilfs_btree_node_size(const struct nilfs_btree *btree)
+static inline int nilfs_btree_node_size(const struct nilfs_btree *btree)
{
return 1 << btree->bt_bmap.b_inode->i_blkbits;
}
static inline int
-nilfs_btree_node_nchildren_min(const struct nilfs_btree *btree,
- const struct nilfs_btree_node *node)
+nilfs_btree_node_nchildren_min(const struct nilfs_btree_node *node,
+ const struct nilfs_btree *btree)
{
- return nilfs_btree_node_root(btree, node) ?
+ return nilfs_btree_node_root(node) ?
NILFS_BTREE_ROOT_NCHILDREN_MIN :
NILFS_BTREE_NODE_NCHILDREN_MIN(nilfs_btree_node_size(btree));
}
static inline int
-nilfs_btree_node_nchildren_max(const struct nilfs_btree *btree,
- const struct nilfs_btree_node *node)
+nilfs_btree_node_nchildren_max(const struct nilfs_btree_node *node,
+ const struct nilfs_btree *btree)
{
- return nilfs_btree_node_root(btree, node) ?
+ return nilfs_btree_node_root(node) ?
NILFS_BTREE_ROOT_NCHILDREN_MAX :
NILFS_BTREE_NODE_NCHILDREN_MAX(nilfs_btree_node_size(btree));
}
static inline __le64 *
-nilfs_btree_node_dkeys(const struct nilfs_btree *btree,
- const struct nilfs_btree_node *node)
+nilfs_btree_node_dkeys(const struct nilfs_btree_node *node)
{
return (__le64 *)((char *)(node + 1) +
- (nilfs_btree_node_root(btree, node) ?
+ (nilfs_btree_node_root(node) ?
0 : NILFS_BTREE_NODE_EXTRA_PAD_SIZE));
}
static inline __le64 *
-nilfs_btree_node_dptrs(const struct nilfs_btree *btree,
- const struct nilfs_btree_node *node)
+nilfs_btree_node_dptrs(const struct nilfs_btree_node *node,
+ const struct nilfs_btree *btree)
{
- return (__le64 *)(nilfs_btree_node_dkeys(btree, node) +
- nilfs_btree_node_nchildren_max(btree, node));
+ return (__le64 *)(nilfs_btree_node_dkeys(node) +
+ nilfs_btree_node_nchildren_max(node, btree));
}
static inline __u64
-nilfs_btree_node_get_key(const struct nilfs_btree *btree,
- const struct nilfs_btree_node *node, int index)
+nilfs_btree_node_get_key(const struct nilfs_btree_node *node, int index)
{
- return nilfs_bmap_dkey_to_key(*(nilfs_btree_node_dkeys(btree, node) +
- index));
+ return nilfs_bmap_dkey_to_key(*(nilfs_btree_node_dkeys(node) + index));
}
static inline void
-nilfs_btree_node_set_key(struct nilfs_btree *btree,
- struct nilfs_btree_node *node, int index, __u64 key)
+nilfs_btree_node_set_key(struct nilfs_btree_node *node, int index, __u64 key)
{
- *(nilfs_btree_node_dkeys(btree, node) + index) =
- nilfs_bmap_key_to_dkey(key);
+ *(nilfs_btree_node_dkeys(node) + index) = nilfs_bmap_key_to_dkey(key);
}
static inline __u64
nilfs_btree_node_get_ptr(const struct nilfs_btree *btree,
- const struct nilfs_btree_node *node,
- int index)
+ const struct nilfs_btree_node *node, int index)
{
- return nilfs_bmap_dptr_to_ptr(*(nilfs_btree_node_dptrs(btree, node) +
+ return nilfs_bmap_dptr_to_ptr(*(nilfs_btree_node_dptrs(node, btree) +
index));
}
static inline void
nilfs_btree_node_set_ptr(struct nilfs_btree *btree,
- struct nilfs_btree_node *node,
- int index,
- __u64 ptr)
+ struct nilfs_btree_node *node, int index, __u64 ptr)
{
- *(nilfs_btree_node_dptrs(btree, node) + index) =
+ *(nilfs_btree_node_dptrs(node, btree) + index) =
nilfs_bmap_ptr_to_dptr(ptr);
}
@@ -283,12 +247,12 @@ static void nilfs_btree_node_init(struct nilfs_btree *btree,
__le64 *dptrs;
int i;
- nilfs_btree_node_set_flags(btree, node, flags);
- nilfs_btree_node_set_level(btree, node, level);
- nilfs_btree_node_set_nchildren(btree, node, nchildren);
+ nilfs_btree_node_set_flags(node, flags);
+ nilfs_btree_node_set_level(node, level);
+ nilfs_btree_node_set_nchildren(node, nchildren);
- dkeys = nilfs_btree_node_dkeys(btree, node);
- dptrs = nilfs_btree_node_dptrs(btree, node);
+ dkeys = nilfs_btree_node_dkeys(node);
+ dptrs = nilfs_btree_node_dptrs(node, btree);
for (i = 0; i < nchildren; i++) {
dkeys[i] = nilfs_bmap_key_to_dkey(keys[i]);
dptrs[i] = nilfs_bmap_ptr_to_dptr(ptrs[i]);
@@ -305,13 +269,13 @@ static void nilfs_btree_node_move_left(struct nilfs_btree *btree,
__le64 *ldptrs, *rdptrs;
int lnchildren, rnchildren;
- ldkeys = nilfs_btree_node_dkeys(btree, left);
- ldptrs = nilfs_btree_node_dptrs(btree, left);
- lnchildren = nilfs_btree_node_get_nchildren(btree, left);
+ ldkeys = nilfs_btree_node_dkeys(left);
+ ldptrs = nilfs_btree_node_dptrs(left, btree);
+ lnchildren = nilfs_btree_node_get_nchildren(left);
- rdkeys = nilfs_btree_node_dkeys(btree, right);
- rdptrs = nilfs_btree_node_dptrs(btree, right);
- rnchildren = nilfs_btree_node_get_nchildren(btree, right);
+ rdkeys = nilfs_btree_node_dkeys(right);
+ rdptrs = nilfs_btree_node_dptrs(right, btree);
+ rnchildren = nilfs_btree_node_get_nchildren(right);
memcpy(ldkeys + lnchildren, rdkeys, n * sizeof(*rdkeys));
memcpy(ldptrs + lnchildren, rdptrs, n * sizeof(*rdptrs));
@@ -320,8 +284,8 @@ static void nilfs_btree_node_move_left(struct nilfs_btree *btree,
lnchildren += n;
rnchildren -= n;
- nilfs_btree_node_set_nchildren(btree, left, lnchildren);
- nilfs_btree_node_set_nchildren(btree, right, rnchildren);
+ nilfs_btree_node_set_nchildren(left, lnchildren);
+ nilfs_btree_node_set_nchildren(right, rnchildren);
}
/* Assume that the buffer heads corresponding to left and right are locked. */
@@ -334,13 +298,13 @@ static void nilfs_btree_node_move_right(struct nilfs_btree *btree,
__le64 *ldptrs, *rdptrs;
int lnchildren, rnchildren;
- ldkeys = nilfs_btree_node_dkeys(btree, left);
- ldptrs = nilfs_btree_node_dptrs(btree, left);
- lnchildren = nilfs_btree_node_get_nchildren(btree, left);
+ ldkeys = nilfs_btree_node_dkeys(left);
+ ldptrs = nilfs_btree_node_dptrs(left, btree);
+ lnchildren = nilfs_btree_node_get_nchildren(left);
- rdkeys = nilfs_btree_node_dkeys(btree, right);
- rdptrs = nilfs_btree_node_dptrs(btree, right);
- rnchildren = nilfs_btree_node_get_nchildren(btree, right);
+ rdkeys = nilfs_btree_node_dkeys(right);
+ rdptrs = nilfs_btree_node_dptrs(right, btree);
+ rnchildren = nilfs_btree_node_get_nchildren(right);
memmove(rdkeys + n, rdkeys, rnchildren * sizeof(*rdkeys));
memmove(rdptrs + n, rdptrs, rnchildren * sizeof(*rdptrs));
@@ -349,8 +313,8 @@ static void nilfs_btree_node_move_right(struct nilfs_btree *btree,
lnchildren -= n;
rnchildren += n;
- nilfs_btree_node_set_nchildren(btree, left, lnchildren);
- nilfs_btree_node_set_nchildren(btree, right, rnchildren);
+ nilfs_btree_node_set_nchildren(left, lnchildren);
+ nilfs_btree_node_set_nchildren(right, rnchildren);
}
/* Assume that the buffer head corresponding to node is locked. */
@@ -362,9 +326,9 @@ static void nilfs_btree_node_insert(struct nilfs_btree *btree,
__le64 *dptrs;
int nchildren;
- dkeys = nilfs_btree_node_dkeys(btree, node);
- dptrs = nilfs_btree_node_dptrs(btree, node);
- nchildren = nilfs_btree_node_get_nchildren(btree, node);
+ dkeys = nilfs_btree_node_dkeys(node);
+ dptrs = nilfs_btree_node_dptrs(node, btree);
+ nchildren = nilfs_btree_node_get_nchildren(node);
if (index < nchildren) {
memmove(dkeys + index + 1, dkeys + index,
(nchildren - index) * sizeof(*dkeys));
@@ -374,7 +338,7 @@ static void nilfs_btree_node_insert(struct nilfs_btree *btree,
dkeys[index] = nilfs_bmap_key_to_dkey(key);
dptrs[index] = nilfs_bmap_ptr_to_dptr(ptr);
nchildren++;
- nilfs_btree_node_set_nchildren(btree, node, nchildren);
+ nilfs_btree_node_set_nchildren(node, nchildren);
}
/* Assume that the buffer head corresponding to node is locked. */
@@ -388,11 +352,11 @@ static void nilfs_btree_node_delete(struct nilfs_btree *btree,
__le64 *dptrs;
int nchildren;
- dkeys = nilfs_btree_node_dkeys(btree, node);
- dptrs = nilfs_btree_node_dptrs(btree, node);
+ dkeys = nilfs_btree_node_dkeys(node);
+ dptrs = nilfs_btree_node_dptrs(node, btree);
key = nilfs_bmap_dkey_to_key(dkeys[index]);
ptr = nilfs_bmap_dptr_to_ptr(dptrs[index]);
- nchildren = nilfs_btree_node_get_nchildren(btree, node);
+ nchildren = nilfs_btree_node_get_nchildren(node);
if (keyp != NULL)
*keyp = key;
if (ptrp != NULL)
@@ -405,11 +369,10 @@ static void nilfs_btree_node_delete(struct nilfs_btree *btree,
(nchildren - index - 1) * sizeof(*dptrs));
}
nchildren--;
- nilfs_btree_node_set_nchildren(btree, node, nchildren);
+ nilfs_btree_node_set_nchildren(node, nchildren);
}
-static int nilfs_btree_node_lookup(const struct nilfs_btree *btree,
- const struct nilfs_btree_node *node,
+static int nilfs_btree_node_lookup(const struct nilfs_btree_node *node,
__u64 key, int *indexp)
{
__u64 nkey;
@@ -417,12 +380,12 @@ static int nilfs_btree_node_lookup(const struct nilfs_btree *btree,
/* binary search */
low = 0;
- high = nilfs_btree_node_get_nchildren(btree, node) - 1;
+ high = nilfs_btree_node_get_nchildren(node) - 1;
index = 0;
s = 0;
while (low <= high) {
index = (low + high) / 2;
- nkey = nilfs_btree_node_get_key(btree, node, index);
+ nkey = nilfs_btree_node_get_key(node, index);
if (nkey == key) {
s = 0;
goto out;
@@ -436,9 +399,8 @@ static int nilfs_btree_node_lookup(const struct nilfs_btree *btree,
}
/* adjust index */
- if (nilfs_btree_node_get_level(btree, node) >
- NILFS_BTREE_LEVEL_NODE_MIN) {
- if ((s > 0) && (index > 0))
+ if (nilfs_btree_node_get_level(node) > NILFS_BTREE_LEVEL_NODE_MIN) {
+ if (s > 0 && index > 0)
index--;
} else if (s < 0)
index++;
@@ -456,25 +418,20 @@ nilfs_btree_get_root(const struct nilfs_btree *btree)
}
static inline struct nilfs_btree_node *
-nilfs_btree_get_nonroot_node(const struct nilfs_btree *btree,
- const struct nilfs_btree_path *path,
- int level)
+nilfs_btree_get_nonroot_node(const struct nilfs_btree_path *path, int level)
{
return (struct nilfs_btree_node *)path[level].bp_bh->b_data;
}
static inline struct nilfs_btree_node *
-nilfs_btree_get_sib_node(const struct nilfs_btree *btree,
- const struct nilfs_btree_path *path,
- int level)
+nilfs_btree_get_sib_node(const struct nilfs_btree_path *path, int level)
{
return (struct nilfs_btree_node *)path[level].bp_sib_bh->b_data;
}
static inline int nilfs_btree_height(const struct nilfs_btree *btree)
{
- return nilfs_btree_node_get_level(btree, nilfs_btree_get_root(btree))
- + 1;
+ return nilfs_btree_node_get_level(nilfs_btree_get_root(btree)) + 1;
}
static inline struct nilfs_btree_node *
@@ -484,7 +441,7 @@ nilfs_btree_get_node(const struct nilfs_btree *btree,
{
return (level == nilfs_btree_height(btree) - 1) ?
nilfs_btree_get_root(btree) :
- nilfs_btree_get_nonroot_node(btree, path, level);
+ nilfs_btree_get_nonroot_node(path, level);
}
static int nilfs_btree_do_lookup(const struct nilfs_btree *btree,
@@ -496,12 +453,11 @@ static int nilfs_btree_do_lookup(const struct nilfs_btree *btree,
int level, index, found, ret;
node = nilfs_btree_get_root(btree);
- level = nilfs_btree_node_get_level(btree, node);
- if ((level < minlevel) ||
- (nilfs_btree_node_get_nchildren(btree, node) <= 0))
+ level = nilfs_btree_node_get_level(node);
+ if (level < minlevel || nilfs_btree_node_get_nchildren(node) <= 0)
return -ENOENT;
- found = nilfs_btree_node_lookup(btree, node, key, &index);
+ found = nilfs_btree_node_lookup(node, key, &index);
ptr = nilfs_btree_node_get_ptr(btree, node, index);
path[level].bp_bh = NULL;
path[level].bp_index = index;
@@ -510,14 +466,13 @@ static int nilfs_btree_do_lookup(const struct nilfs_btree *btree,
ret = nilfs_btree_get_block(btree, ptr, &path[level].bp_bh);
if (ret < 0)
return ret;
- node = nilfs_btree_get_nonroot_node(btree, path, level);
- BUG_ON(level != nilfs_btree_node_get_level(btree, node));
+ node = nilfs_btree_get_nonroot_node(path, level);
+ BUG_ON(level != nilfs_btree_node_get_level(node));
if (!found)
- found = nilfs_btree_node_lookup(btree, node, key,
- &index);
+ found = nilfs_btree_node_lookup(node, key, &index);
else
index = 0;
- if (index < nilfs_btree_node_nchildren_max(btree, node))
+ if (index < nilfs_btree_node_nchildren_max(node, btree))
ptr = nilfs_btree_node_get_ptr(btree, node, index);
else {
WARN_ON(found || level != NILFS_BTREE_LEVEL_NODE_MIN);
@@ -544,10 +499,10 @@ static int nilfs_btree_do_lookup_last(const struct nilfs_btree *btree,
int index, level, ret;
node = nilfs_btree_get_root(btree);
- index = nilfs_btree_node_get_nchildren(btree, node) - 1;
+ index = nilfs_btree_node_get_nchildren(node) - 1;
if (index < 0)
return -ENOENT;
- level = nilfs_btree_node_get_level(btree, node);
+ level = nilfs_btree_node_get_level(node);
ptr = nilfs_btree_node_get_ptr(btree, node, index);
path[level].bp_bh = NULL;
path[level].bp_index = index;
@@ -556,15 +511,15 @@ static int nilfs_btree_do_lookup_last(const struct nilfs_btree *btree,
ret = nilfs_btree_get_block(btree, ptr, &path[level].bp_bh);
if (ret < 0)
return ret;
- node = nilfs_btree_get_nonroot_node(btree, path, level);
- BUG_ON(level != nilfs_btree_node_get_level(btree, node));
- index = nilfs_btree_node_get_nchildren(btree, node) - 1;
+ node = nilfs_btree_get_nonroot_node(path, level);
+ BUG_ON(level != nilfs_btree_node_get_level(node));
+ index = nilfs_btree_node_get_nchildren(node) - 1;
ptr = nilfs_btree_node_get_ptr(btree, node, index);
path[level].bp_index = index;
}
if (keyp != NULL)
- *keyp = nilfs_btree_node_get_key(btree, node, index);
+ *keyp = nilfs_btree_node_get_key(node, index);
if (ptrp != NULL)
*ptrp = ptr;
@@ -580,18 +535,18 @@ static int nilfs_btree_lookup(const struct nilfs_bmap *bmap,
int ret;
btree = (struct nilfs_btree *)bmap;
- path = nilfs_btree_alloc_path(btree);
+ path = nilfs_btree_alloc_path();
if (path == NULL)
return -ENOMEM;
- nilfs_btree_init_path(btree, path);
+ nilfs_btree_init_path(path);
ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level);
if (ptrp != NULL)
*ptrp = ptr;
- nilfs_btree_clear_path(btree, path);
- nilfs_btree_free_path(btree, path);
+ nilfs_btree_release_path(path);
+ nilfs_btree_free_path(path);
return ret;
}
@@ -608,10 +563,10 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *bmap,
int level = NILFS_BTREE_LEVEL_NODE_MIN;
int ret, cnt, index, maxlevel;
- path = nilfs_btree_alloc_path(btree);
+ path = nilfs_btree_alloc_path();
if (path == NULL)
return -ENOMEM;
- nilfs_btree_init_path(btree, path);
+ nilfs_btree_init_path(path);
ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level);
if (ret < 0)
goto out;
@@ -631,8 +586,8 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *bmap,
node = nilfs_btree_get_node(btree, path, level);
index = path[level].bp_index + 1;
for (;;) {
- while (index < nilfs_btree_node_get_nchildren(btree, node)) {
- if (nilfs_btree_node_get_key(btree, node, index) !=
+ while (index < nilfs_btree_node_get_nchildren(node)) {
+ if (nilfs_btree_node_get_key(node, index) !=
key + cnt)
goto end;
ptr2 = nilfs_btree_node_get_ptr(btree, node, index);
@@ -653,8 +608,8 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *bmap,
/* look-up right sibling node */
node = nilfs_btree_get_node(btree, path, level + 1);
index = path[level + 1].bp_index + 1;
- if (index >= nilfs_btree_node_get_nchildren(btree, node) ||
- nilfs_btree_node_get_key(btree, node, index) != key + cnt)
+ if (index >= nilfs_btree_node_get_nchildren(node) ||
+ nilfs_btree_node_get_key(node, index) != key + cnt)
break;
ptr2 = nilfs_btree_node_get_ptr(btree, node, index);
path[level + 1].bp_index = index;
@@ -664,7 +619,7 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *bmap,
ret = nilfs_btree_get_block(btree, ptr2, &path[level].bp_bh);
if (ret < 0)
goto out;
- node = nilfs_btree_get_nonroot_node(btree, path, level);
+ node = nilfs_btree_get_nonroot_node(path, level);
index = 0;
path[level].bp_index = index;
}
@@ -672,8 +627,8 @@ static int nilfs_btree_lookup_contig(const struct nilfs_bmap *bmap,
*ptrp = ptr;
ret = cnt;
out:
- nilfs_btree_clear_path(btree, path);
- nilfs_btree_free_path(btree, path);
+ nilfs_btree_release_path(path);
+ nilfs_btree_free_path(path);
return ret;
}
@@ -685,9 +640,7 @@ static void nilfs_btree_promote_key(struct nilfs_btree *btree,
do {
lock_buffer(path[level].bp_bh);
nilfs_btree_node_set_key(
- btree,
- nilfs_btree_get_nonroot_node(
- btree, path, level),
+ nilfs_btree_get_nonroot_node(path, level),
path[level].bp_index, key);
if (!buffer_dirty(path[level].bp_bh))
nilfs_btnode_mark_dirty(path[level].bp_bh);
@@ -698,8 +651,7 @@ static void nilfs_btree_promote_key(struct nilfs_btree *btree,
/* root */
if (level == nilfs_btree_height(btree) - 1) {
- nilfs_btree_node_set_key(btree,
- nilfs_btree_get_root(btree),
+ nilfs_btree_node_set_key(nilfs_btree_get_root(btree),
path[level].bp_index, key);
}
}
@@ -712,7 +664,7 @@ static void nilfs_btree_do_insert(struct nilfs_btree *btree,
if (level < nilfs_btree_height(btree) - 1) {
lock_buffer(path[level].bp_bh);
- node = nilfs_btree_get_nonroot_node(btree, path, level);
+ node = nilfs_btree_get_nonroot_node(path, level);
nilfs_btree_node_insert(btree, node, *keyp, *ptrp,
path[level].bp_index);
if (!buffer_dirty(path[level].bp_bh))
@@ -721,8 +673,8 @@ static void nilfs_btree_do_insert(struct nilfs_btree *btree,
if (path[level].bp_index == 0)
nilfs_btree_promote_key(btree, path, level + 1,
- nilfs_btree_node_get_key(
- btree, node, 0));
+ nilfs_btree_node_get_key(node,
+ 0));
} else {
node = nilfs_btree_get_root(btree);
nilfs_btree_node_insert(btree, node, *keyp, *ptrp,
@@ -740,10 +692,10 @@ static void nilfs_btree_carry_left(struct nilfs_btree *btree,
lock_buffer(path[level].bp_bh);
lock_buffer(path[level].bp_sib_bh);
- node = nilfs_btree_get_nonroot_node(btree, path, level);
- left = nilfs_btree_get_sib_node(btree, path, level);
- nchildren = nilfs_btree_node_get_nchildren(btree, node);
- lnchildren = nilfs_btree_node_get_nchildren(btree, left);
+ node = nilfs_btree_get_nonroot_node(path, level);
+ left = nilfs_btree_get_sib_node(path, level);
+ nchildren = nilfs_btree_node_get_nchildren(node);
+ lnchildren = nilfs_btree_node_get_nchildren(left);
move = 0;
n = (nchildren + lnchildren + 1) / 2 - lnchildren;
@@ -764,7 +716,7 @@ static void nilfs_btree_carry_left(struct nilfs_btree *btree,
unlock_buffer(path[level].bp_sib_bh);
nilfs_btree_promote_key(btree, path, level + 1,
- nilfs_btree_node_get_key(btree, node, 0));
+ nilfs_btree_node_get_key(node, 0));
if (move) {
brelse(path[level].bp_bh);
@@ -791,10 +743,10 @@ static void nilfs_btree_carry_right(struct nilfs_btree *btree,
lock_buffer(path[level].bp_bh);
lock_buffer(path[level].bp_sib_bh);
- node = nilfs_btree_get_nonroot_node(btree, path, level);
- right = nilfs_btree_get_sib_node(btree, path, level);
- nchildren = nilfs_btree_node_get_nchildren(btree, node);
- rnchildren = nilfs_btree_node_get_nchildren(btree, right);
+ node = nilfs_btree_get_nonroot_node(path, level);
+ right = nilfs_btree_get_sib_node(path, level);
+ nchildren = nilfs_btree_node_get_nchildren(node);
+ rnchildren = nilfs_btree_node_get_nchildren(right);
move = 0;
n = (nchildren + rnchildren + 1) / 2 - rnchildren;
@@ -816,15 +768,14 @@ static void nilfs_btree_carry_right(struct nilfs_btree *btree,
path[level + 1].bp_index++;
nilfs_btree_promote_key(btree, path, level + 1,
- nilfs_btree_node_get_key(btree, right, 0));
+ nilfs_btree_node_get_key(right, 0));
path[level + 1].bp_index--;
if (move) {
brelse(path[level].bp_bh);
path[level].bp_bh = path[level].bp_sib_bh;
path[level].bp_sib_bh = NULL;
- path[level].bp_index -=
- nilfs_btree_node_get_nchildren(btree, node);
+ path[level].bp_index -= nilfs_btree_node_get_nchildren(node);
path[level + 1].bp_index++;
} else {
brelse(path[level].bp_sib_bh);
@@ -846,9 +797,9 @@ static void nilfs_btree_split(struct nilfs_btree *btree,
lock_buffer(path[level].bp_bh);
lock_buffer(path[level].bp_sib_bh);
- node = nilfs_btree_get_nonroot_node(btree, path, level);
- right = nilfs_btree_get_sib_node(btree, path, level);
- nchildren = nilfs_btree_node_get_nchildren(btree, node);
+ node = nilfs_btree_get_nonroot_node(path, level);
+ right = nilfs_btree_get_sib_node(path, level);
+ nchildren = nilfs_btree_node_get_nchildren(node);
move = 0;
n = (nchildren + 1) / 2;
@@ -867,16 +818,15 @@ static void nilfs_btree_split(struct nilfs_btree *btree,
unlock_buffer(path[level].bp_bh);
unlock_buffer(path[level].bp_sib_bh);
- newkey = nilfs_btree_node_get_key(btree, right, 0);
+ newkey = nilfs_btree_node_get_key(right, 0);
newptr = path[level].bp_newreq.bpr_ptr;
if (move) {
- path[level].bp_index -=
- nilfs_btree_node_get_nchildren(btree, node);
+ path[level].bp_index -= nilfs_btree_node_get_nchildren(node);
nilfs_btree_node_insert(btree, right, *keyp, *ptrp,
path[level].bp_index);
- *keyp = nilfs_btree_node_get_key(btree, right, 0);
+ *keyp = nilfs_btree_node_get_key(right, 0);
*ptrp = path[level].bp_newreq.bpr_ptr;
brelse(path[level].bp_bh);
@@ -885,7 +835,7 @@ static void nilfs_btree_split(struct nilfs_btree *btree,
} else {
nilfs_btree_do_insert(btree, path, level, keyp, ptrp);
- *keyp = nilfs_btree_node_get_key(btree, right, 0);
+ *keyp = nilfs_btree_node_get_key(right, 0);
*ptrp = path[level].bp_newreq.bpr_ptr;
brelse(path[level].bp_sib_bh);
@@ -905,12 +855,12 @@ static void nilfs_btree_grow(struct nilfs_btree *btree,
lock_buffer(path[level].bp_sib_bh);
root = nilfs_btree_get_root(btree);
- child = nilfs_btree_get_sib_node(btree, path, level);
+ child = nilfs_btree_get_sib_node(path, level);
- n = nilfs_btree_node_get_nchildren(btree, root);
+ n = nilfs_btree_node_get_nchildren(root);
nilfs_btree_node_move_right(btree, root, child, n);
- nilfs_btree_node_set_level(btree, root, level + 1);
+ nilfs_btree_node_set_level(root, level + 1);
if (!buffer_dirty(path[level].bp_sib_bh))
nilfs_btnode_mark_dirty(path[level].bp_sib_bh);
@@ -922,7 +872,7 @@ static void nilfs_btree_grow(struct nilfs_btree *btree,
nilfs_btree_do_insert(btree, path, level, keyp, ptrp);
- *keyp = nilfs_btree_node_get_key(btree, child, 0);
+ *keyp = nilfs_btree_node_get_key(child, 0);
*ptrp = path[level].bp_newreq.bpr_ptr;
}
@@ -990,26 +940,29 @@ static int nilfs_btree_prepare_insert(struct nilfs_btree *btree,
struct nilfs_btree_node *node, *parent, *sib;
__u64 sibptr;
int pindex, level, ret;
+ struct inode *dat = NULL;
stats->bs_nblocks = 0;
level = NILFS_BTREE_LEVEL_DATA;
/* allocate a new ptr for data block */
- if (NILFS_BMAP_USE_VBN(&btree->bt_bmap))
+ if (NILFS_BMAP_USE_VBN(&btree->bt_bmap)) {
path[level].bp_newreq.bpr_ptr =
nilfs_btree_find_target_v(btree, path, key);
+ dat = nilfs_bmap_get_dat(&btree->bt_bmap);
+ }
ret = nilfs_bmap_prepare_alloc_ptr(&btree->bt_bmap,
- &path[level].bp_newreq);
+ &path[level].bp_newreq, dat);
if (ret < 0)
goto err_out_data;
for (level = NILFS_BTREE_LEVEL_NODE_MIN;
level < nilfs_btree_height(btree) - 1;
level++) {
- node = nilfs_btree_get_nonroot_node(btree, path, level);
- if (nilfs_btree_node_get_nchildren(btree, node) <
- nilfs_btree_node_nchildren_max(btree, node)) {
+ node = nilfs_btree_get_nonroot_node(path, level);
+ if (nilfs_btree_node_get_nchildren(node) <
+ nilfs_btree_node_nchildren_max(node, btree)) {
path[level].bp_op = nilfs_btree_do_insert;
stats->bs_nblocks++;
goto out;
@@ -1026,8 +979,8 @@ static int nilfs_btree_prepare_insert(struct nilfs_btree *btree,
if (ret < 0)
goto err_out_child_node;
sib = (struct nilfs_btree_node *)bh->b_data;
- if (nilfs_btree_node_get_nchildren(btree, sib) <
- nilfs_btree_node_nchildren_max(btree, sib)) {
+ if (nilfs_btree_node_get_nchildren(sib) <
+ nilfs_btree_node_nchildren_max(sib, btree)) {
path[level].bp_sib_bh = bh;
path[level].bp_op = nilfs_btree_carry_left;
stats->bs_nblocks++;
@@ -1038,15 +991,15 @@ static int nilfs_btree_prepare_insert(struct nilfs_btree *btree,
/* right sibling */
if (pindex <
- nilfs_btree_node_get_nchildren(btree, parent) - 1) {
+ nilfs_btree_node_get_nchildren(parent) - 1) {
sibptr = nilfs_btree_node_get_ptr(btree, parent,
pindex + 1);
ret = nilfs_btree_get_block(btree, sibptr, &bh);
if (ret < 0)
goto err_out_child_node;
sib = (struct nilfs_btree_node *)bh->b_data;
- if (nilfs_btree_node_get_nchildren(btree, sib) <
- nilfs_btree_node_nchildren_max(btree, sib)) {
+ if (nilfs_btree_node_get_nchildren(sib) <
+ nilfs_btree_node_nchildren_max(sib, btree)) {
path[level].bp_sib_bh = bh;
path[level].bp_op = nilfs_btree_carry_right;
stats->bs_nblocks++;
@@ -1059,7 +1012,7 @@ static int nilfs_btree_prepare_insert(struct nilfs_btree *btree,
path[level].bp_newreq.bpr_ptr =
path[level - 1].bp_newreq.bpr_ptr + 1;
ret = nilfs_bmap_prepare_alloc_ptr(&btree->bt_bmap,
- &path[level].bp_newreq);
+ &path[level].bp_newreq, dat);
if (ret < 0)
goto err_out_child_node;
ret = nilfs_btree_get_new_block(btree,
@@ -1081,8 +1034,8 @@ static int nilfs_btree_prepare_insert(struct nilfs_btree *btree,
/* root */
node = nilfs_btree_get_root(btree);
- if (nilfs_btree_node_get_nchildren(btree, node) <
- nilfs_btree_node_nchildren_max(btree, node)) {
+ if (nilfs_btree_node_get_nchildren(node) <
+ nilfs_btree_node_nchildren_max(node, btree)) {
path[level].bp_op = nilfs_btree_do_insert;
stats->bs_nblocks++;
goto out;
@@ -1091,7 +1044,7 @@ static int nilfs_btree_prepare_insert(struct nilfs_btree *btree,
/* grow */
path[level].bp_newreq.bpr_ptr = path[level - 1].bp_newreq.bpr_ptr + 1;
ret = nilfs_bmap_prepare_alloc_ptr(&btree->bt_bmap,
- &path[level].bp_newreq);
+ &path[level].bp_newreq, dat);
if (ret < 0)
goto err_out_child_node;
ret = nilfs_btree_get_new_block(btree, path[level].bp_newreq.bpr_ptr,
@@ -1119,16 +1072,18 @@ static int nilfs_btree_prepare_insert(struct nilfs_btree *btree,
/* error */
err_out_curr_node:
- nilfs_bmap_abort_alloc_ptr(&btree->bt_bmap, &path[level].bp_newreq);
+ nilfs_bmap_abort_alloc_ptr(&btree->bt_bmap, &path[level].bp_newreq,
+ dat);
err_out_child_node:
for (level--; level > NILFS_BTREE_LEVEL_DATA; level--) {
nilfs_btnode_delete(path[level].bp_sib_bh);
nilfs_bmap_abort_alloc_ptr(&btree->bt_bmap,
- &path[level].bp_newreq);
+ &path[level].bp_newreq, dat);
}
- nilfs_bmap_abort_alloc_ptr(&btree->bt_bmap, &path[level].bp_newreq);
+ nilfs_bmap_abort_alloc_ptr(&btree->bt_bmap, &path[level].bp_newreq,
+ dat);
err_out_data:
*levelp = level;
stats->bs_nblocks = 0;
@@ -1139,16 +1094,19 @@ static void nilfs_btree_commit_insert(struct nilfs_btree *btree,
struct nilfs_btree_path *path,
int maxlevel, __u64 key, __u64 ptr)
{
+ struct inode *dat = NULL;
int level;
set_buffer_nilfs_volatile((struct buffer_head *)((unsigned long)ptr));
ptr = path[NILFS_BTREE_LEVEL_DATA].bp_newreq.bpr_ptr;
- if (NILFS_BMAP_USE_VBN(&btree->bt_bmap))
+ if (NILFS_BMAP_USE_VBN(&btree->bt_bmap)) {
nilfs_btree_set_target_v(btree, key, ptr);
+ dat = nilfs_bmap_get_dat(&btree->bt_bmap);
+ }
for (level = NILFS_BTREE_LEVEL_NODE_MIN; level <= maxlevel; level++) {
nilfs_bmap_commit_alloc_ptr(&btree->bt_bmap,
- &path[level - 1].bp_newreq);
+ &path[level - 1].bp_newreq, dat);
path[level].bp_op(btree, path, level, &key, &ptr);
}
@@ -1164,10 +1122,10 @@ static int nilfs_btree_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr)
int level, ret;
btree = (struct nilfs_btree *)bmap;
- path = nilfs_btree_alloc_path(btree);
+ path = nilfs_btree_alloc_path();
if (path == NULL)
return -ENOMEM;
- nilfs_btree_init_path(btree, path);
+ nilfs_btree_init_path(path);
ret = nilfs_btree_do_lookup(btree, path, key, NULL,
NILFS_BTREE_LEVEL_NODE_MIN);
@@ -1184,8 +1142,8 @@ static int nilfs_btree_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr)
nilfs_bmap_add_blocks(bmap, stats.bs_nblocks);
out:
- nilfs_btree_clear_path(btree, path);
- nilfs_btree_free_path(btree, path);
+ nilfs_btree_release_path(path);
+ nilfs_btree_free_path(path);
return ret;
}
@@ -1197,7 +1155,7 @@ static void nilfs_btree_do_delete(struct nilfs_btree *btree,
if (level < nilfs_btree_height(btree) - 1) {
lock_buffer(path[level].bp_bh);
- node = nilfs_btree_get_nonroot_node(btree, path, level);
+ node = nilfs_btree_get_nonroot_node(path, level);
nilfs_btree_node_delete(btree, node, keyp, ptrp,
path[level].bp_index);
if (!buffer_dirty(path[level].bp_bh))
@@ -1205,7 +1163,7 @@ static void nilfs_btree_do_delete(struct nilfs_btree *btree,
unlock_buffer(path[level].bp_bh);
if (path[level].bp_index == 0)
nilfs_btree_promote_key(btree, path, level + 1,
- nilfs_btree_node_get_key(btree, node, 0));
+ nilfs_btree_node_get_key(node, 0));
} else {
node = nilfs_btree_get_root(btree);
nilfs_btree_node_delete(btree, node, keyp, ptrp,
@@ -1225,10 +1183,10 @@ static void nilfs_btree_borrow_left(struct nilfs_btree *btree,
lock_buffer(path[level].bp_bh);
lock_buffer(path[level].bp_sib_bh);
- node = nilfs_btree_get_nonroot_node(btree, path, level);
- left = nilfs_btree_get_sib_node(btree, path, level);
- nchildren = nilfs_btree_node_get_nchildren(btree, node);
- lnchildren = nilfs_btree_node_get_nchildren(btree, left);
+ node = nilfs_btree_get_nonroot_node(path, level);
+ left = nilfs_btree_get_sib_node(path, level);
+ nchildren = nilfs_btree_node_get_nchildren(node);
+ lnchildren = nilfs_btree_node_get_nchildren(left);
n = (nchildren + lnchildren) / 2 - nchildren;
@@ -1243,7 +1201,7 @@ static void nilfs_btree_borrow_left(struct nilfs_btree *btree,
unlock_buffer(path[level].bp_sib_bh);
nilfs_btree_promote_key(btree, path, level + 1,
- nilfs_btree_node_get_key(btree, node, 0));
+ nilfs_btree_node_get_key(node, 0));
brelse(path[level].bp_sib_bh);
path[level].bp_sib_bh = NULL;
@@ -1262,10 +1220,10 @@ static void nilfs_btree_borrow_right(struct nilfs_btree *btree,
lock_buffer(path[level].bp_bh);
lock_buffer(path[level].bp_sib_bh);
- node = nilfs_btree_get_nonroot_node(btree, path, level);
- right = nilfs_btree_get_sib_node(btree, path, level);
- nchildren = nilfs_btree_node_get_nchildren(btree, node);
- rnchildren = nilfs_btree_node_get_nchildren(btree, right);
+ node = nilfs_btree_get_nonroot_node(path, level);
+ right = nilfs_btree_get_sib_node(path, level);
+ nchildren = nilfs_btree_node_get_nchildren(node);
+ rnchildren = nilfs_btree_node_get_nchildren(right);
n = (nchildren + rnchildren) / 2 - nchildren;
@@ -1281,7 +1239,7 @@ static void nilfs_btree_borrow_right(struct nilfs_btree *btree,
path[level + 1].bp_index++;
nilfs_btree_promote_key(btree, path, level + 1,
- nilfs_btree_node_get_key(btree, right, 0));
+ nilfs_btree_node_get_key(right, 0));
path[level + 1].bp_index--;
brelse(path[level].bp_sib_bh);
@@ -1300,10 +1258,10 @@ static void nilfs_btree_concat_left(struct nilfs_btree *btree,
lock_buffer(path[level].bp_bh);
lock_buffer(path[level].bp_sib_bh);
- node = nilfs_btree_get_nonroot_node(btree, path, level);
- left = nilfs_btree_get_sib_node(btree, path, level);
+ node = nilfs_btree_get_nonroot_node(path, level);
+ left = nilfs_btree_get_sib_node(path, level);
- n = nilfs_btree_node_get_nchildren(btree, node);
+ n = nilfs_btree_node_get_nchildren(node);
nilfs_btree_node_move_left(btree, left, node, n);
@@ -1316,7 +1274,7 @@ static void nilfs_btree_concat_left(struct nilfs_btree *btree,
nilfs_btnode_delete(path[level].bp_bh);
path[level].bp_bh = path[level].bp_sib_bh;
path[level].bp_sib_bh = NULL;
- path[level].bp_index += nilfs_btree_node_get_nchildren(btree, left);
+ path[level].bp_index += nilfs_btree_node_get_nchildren(left);
}
static void nilfs_btree_concat_right(struct nilfs_btree *btree,
@@ -1331,10 +1289,10 @@ static void nilfs_btree_concat_right(struct nilfs_btree *btree,
lock_buffer(path[level].bp_bh);
lock_buffer(path[level].bp_sib_bh);
- node = nilfs_btree_get_nonroot_node(btree, path, level);
- right = nilfs_btree_get_sib_node(btree, path, level);
+ node = nilfs_btree_get_nonroot_node(path, level);
+ right = nilfs_btree_get_sib_node(path, level);
- n = nilfs_btree_node_get_nchildren(btree, right);
+ n = nilfs_btree_node_get_nchildren(right);
nilfs_btree_node_move_left(btree, node, right, n);
@@ -1360,11 +1318,11 @@ static void nilfs_btree_shrink(struct nilfs_btree *btree,
lock_buffer(path[level].bp_bh);
root = nilfs_btree_get_root(btree);
- child = nilfs_btree_get_nonroot_node(btree, path, level);
+ child = nilfs_btree_get_nonroot_node(path, level);
nilfs_btree_node_delete(btree, root, NULL, NULL, 0);
- nilfs_btree_node_set_level(btree, root, level);
- n = nilfs_btree_node_get_nchildren(btree, child);
+ nilfs_btree_node_set_level(root, level);
+ n = nilfs_btree_node_get_nchildren(child);
nilfs_btree_node_move_left(btree, root, child, n);
unlock_buffer(path[level].bp_bh);
@@ -1376,7 +1334,8 @@ static void nilfs_btree_shrink(struct nilfs_btree *btree,
static int nilfs_btree_prepare_delete(struct nilfs_btree *btree,
struct nilfs_btree_path *path,
int *levelp,
- struct nilfs_bmap_stats *stats)
+ struct nilfs_bmap_stats *stats,
+ struct inode *dat)
{
struct buffer_head *bh;
struct nilfs_btree_node *node, *parent, *sib;
@@ -1388,17 +1347,17 @@ static int nilfs_btree_prepare_delete(struct nilfs_btree *btree,
for (level = NILFS_BTREE_LEVEL_NODE_MIN;
level < nilfs_btree_height(btree) - 1;
level++) {
- node = nilfs_btree_get_nonroot_node(btree, path, level);
+ node = nilfs_btree_get_nonroot_node(path, level);
path[level].bp_oldreq.bpr_ptr =
nilfs_btree_node_get_ptr(btree, node,
path[level].bp_index);
ret = nilfs_bmap_prepare_end_ptr(&btree->bt_bmap,
- &path[level].bp_oldreq);
+ &path[level].bp_oldreq, dat);
if (ret < 0)
goto err_out_child_node;
- if (nilfs_btree_node_get_nchildren(btree, node) >
- nilfs_btree_node_nchildren_min(btree, node)) {
+ if (nilfs_btree_node_get_nchildren(node) >
+ nilfs_btree_node_nchildren_min(node, btree)) {
path[level].bp_op = nilfs_btree_do_delete;
stats->bs_nblocks++;
goto out;
@@ -1415,8 +1374,8 @@ static int nilfs_btree_prepare_delete(struct nilfs_btree *btree,
if (ret < 0)
goto err_out_curr_node;
sib = (struct nilfs_btree_node *)bh->b_data;
- if (nilfs_btree_node_get_nchildren(btree, sib) >
- nilfs_btree_node_nchildren_min(btree, sib)) {
+ if (nilfs_btree_node_get_nchildren(sib) >
+ nilfs_btree_node_nchildren_min(sib, btree)) {
path[level].bp_sib_bh = bh;
path[level].bp_op = nilfs_btree_borrow_left;
stats->bs_nblocks++;
@@ -1428,7 +1387,7 @@ static int nilfs_btree_prepare_delete(struct nilfs_btree *btree,
/* continue; */
}
} else if (pindex <
- nilfs_btree_node_get_nchildren(btree, parent) - 1) {
+ nilfs_btree_node_get_nchildren(parent) - 1) {
/* right sibling */
sibptr = nilfs_btree_node_get_ptr(btree, parent,
pindex + 1);
@@ -1436,8 +1395,8 @@ static int nilfs_btree_prepare_delete(struct nilfs_btree *btree,
if (ret < 0)
goto err_out_curr_node;
sib = (struct nilfs_btree_node *)bh->b_data;
- if (nilfs_btree_node_get_nchildren(btree, sib) >
- nilfs_btree_node_nchildren_min(btree, sib)) {
+ if (nilfs_btree_node_get_nchildren(sib) >
+ nilfs_btree_node_nchildren_min(sib, btree)) {
path[level].bp_sib_bh = bh;
path[level].bp_op = nilfs_btree_borrow_right;
stats->bs_nblocks++;
@@ -1452,7 +1411,7 @@ static int nilfs_btree_prepare_delete(struct nilfs_btree *btree,
/* no siblings */
/* the only child of the root node */
WARN_ON(level != nilfs_btree_height(btree) - 2);
- if (nilfs_btree_node_get_nchildren(btree, node) - 1 <=
+ if (nilfs_btree_node_get_nchildren(node) - 1 <=
NILFS_BTREE_ROOT_NCHILDREN_MAX) {
path[level].bp_op = nilfs_btree_shrink;
stats->bs_nblocks += 2;
@@ -1471,7 +1430,7 @@ static int nilfs_btree_prepare_delete(struct nilfs_btree *btree,
nilfs_btree_node_get_ptr(btree, node, path[level].bp_index);
ret = nilfs_bmap_prepare_end_ptr(&btree->bt_bmap,
- &path[level].bp_oldreq);
+ &path[level].bp_oldreq, dat);
if (ret < 0)
goto err_out_child_node;
@@ -1486,12 +1445,12 @@ static int nilfs_btree_prepare_delete(struct nilfs_btree *btree,
/* error */
err_out_curr_node:
- nilfs_bmap_abort_end_ptr(&btree->bt_bmap, &path[level].bp_oldreq);
+ nilfs_bmap_abort_end_ptr(&btree->bt_bmap, &path[level].bp_oldreq, dat);
err_out_child_node:
for (level--; level >= NILFS_BTREE_LEVEL_NODE_MIN; level--) {
brelse(path[level].bp_sib_bh);
nilfs_bmap_abort_end_ptr(&btree->bt_bmap,
- &path[level].bp_oldreq);
+ &path[level].bp_oldreq, dat);
}
*levelp = level;
stats->bs_nblocks = 0;
@@ -1500,13 +1459,13 @@ static int nilfs_btree_prepare_delete(struct nilfs_btree *btree,
static void nilfs_btree_commit_delete(struct nilfs_btree *btree,
struct nilfs_btree_path *path,
- int maxlevel)
+ int maxlevel, struct inode *dat)
{
int level;
for (level = NILFS_BTREE_LEVEL_NODE_MIN; level <= maxlevel; level++) {
nilfs_bmap_commit_end_ptr(&btree->bt_bmap,
- &path[level].bp_oldreq);
+ &path[level].bp_oldreq, dat);
path[level].bp_op(btree, path, level, NULL, NULL);
}
@@ -1520,27 +1479,32 @@ static int nilfs_btree_delete(struct nilfs_bmap *bmap, __u64 key)
struct nilfs_btree *btree;
struct nilfs_btree_path *path;
struct nilfs_bmap_stats stats;
+ struct inode *dat;
int level, ret;
btree = (struct nilfs_btree *)bmap;
- path = nilfs_btree_alloc_path(btree);
+ path = nilfs_btree_alloc_path();
if (path == NULL)
return -ENOMEM;
- nilfs_btree_init_path(btree, path);
+ nilfs_btree_init_path(path);
ret = nilfs_btree_do_lookup(btree, path, key, NULL,
NILFS_BTREE_LEVEL_NODE_MIN);
if (ret < 0)
goto out;
- ret = nilfs_btree_prepare_delete(btree, path, &level, &stats);
+
+ dat = NILFS_BMAP_USE_VBN(&btree->bt_bmap) ?
+ nilfs_bmap_get_dat(&btree->bt_bmap) : NULL;
+
+ ret = nilfs_btree_prepare_delete(btree, path, &level, &stats, dat);
if (ret < 0)
goto out;
- nilfs_btree_commit_delete(btree, path, level);
+ nilfs_btree_commit_delete(btree, path, level, dat);
nilfs_bmap_sub_blocks(bmap, stats.bs_nblocks);
out:
- nilfs_btree_clear_path(btree, path);
- nilfs_btree_free_path(btree, path);
+ nilfs_btree_release_path(path);
+ nilfs_btree_free_path(path);
return ret;
}
@@ -1551,15 +1515,15 @@ static int nilfs_btree_last_key(const struct nilfs_bmap *bmap, __u64 *keyp)
int ret;
btree = (struct nilfs_btree *)bmap;
- path = nilfs_btree_alloc_path(btree);
+ path = nilfs_btree_alloc_path();
if (path == NULL)
return -ENOMEM;
- nilfs_btree_init_path(btree, path);
+ nilfs_btree_init_path(path);
ret = nilfs_btree_do_lookup_last(btree, path, keyp, NULL);
- nilfs_btree_clear_path(btree, path);
- nilfs_btree_free_path(btree, path);
+ nilfs_btree_release_path(path);
+ nilfs_btree_free_path(path);
return ret;
}
@@ -1581,7 +1545,7 @@ static int nilfs_btree_check_delete(struct nilfs_bmap *bmap, __u64 key)
node = root;
break;
case 3:
- nchildren = nilfs_btree_node_get_nchildren(btree, root);
+ nchildren = nilfs_btree_node_get_nchildren(root);
if (nchildren > 1)
return 0;
ptr = nilfs_btree_node_get_ptr(btree, root, nchildren - 1);
@@ -1594,10 +1558,10 @@ static int nilfs_btree_check_delete(struct nilfs_bmap *bmap, __u64 key)
return 0;
}
- nchildren = nilfs_btree_node_get_nchildren(btree, node);
- maxkey = nilfs_btree_node_get_key(btree, node, nchildren - 1);
+ nchildren = nilfs_btree_node_get_nchildren(node);
+ maxkey = nilfs_btree_node_get_key(node, nchildren - 1);
nextmaxkey = (nchildren > 1) ?
- nilfs_btree_node_get_key(btree, node, nchildren - 2) : 0;
+ nilfs_btree_node_get_key(node, nchildren - 2) : 0;
if (bh != NULL)
brelse(bh);
@@ -1623,7 +1587,7 @@ static int nilfs_btree_gather_data(struct nilfs_bmap *bmap,
node = root;
break;
case 3:
- nchildren = nilfs_btree_node_get_nchildren(btree, root);
+ nchildren = nilfs_btree_node_get_nchildren(root);
WARN_ON(nchildren > 1);
ptr = nilfs_btree_node_get_ptr(btree, root, nchildren - 1);
ret = nilfs_btree_get_block(btree, ptr, &bh);
@@ -1636,11 +1600,11 @@ static int nilfs_btree_gather_data(struct nilfs_bmap *bmap,
return -EINVAL;
}
- nchildren = nilfs_btree_node_get_nchildren(btree, node);
+ nchildren = nilfs_btree_node_get_nchildren(node);
if (nchildren < nitems)
nitems = nchildren;
- dkeys = nilfs_btree_node_dkeys(btree, node);
- dptrs = nilfs_btree_node_dptrs(btree, node);
+ dkeys = nilfs_btree_node_dkeys(node);
+ dptrs = nilfs_btree_node_dptrs(node, btree);
for (i = 0; i < nitems; i++) {
keys[i] = nilfs_bmap_dkey_to_key(dkeys[i]);
ptrs[i] = nilfs_bmap_dptr_to_ptr(dptrs[i]);
@@ -1660,18 +1624,20 @@ nilfs_btree_prepare_convert_and_insert(struct nilfs_bmap *bmap, __u64 key,
struct nilfs_bmap_stats *stats)
{
struct buffer_head *bh;
- struct nilfs_btree *btree;
+ struct nilfs_btree *btree = (struct nilfs_btree *)bmap;
+ struct inode *dat = NULL;
int ret;
- btree = (struct nilfs_btree *)bmap;
stats->bs_nblocks = 0;
/* for data */
/* cannot find near ptr */
- if (NILFS_BMAP_USE_VBN(bmap))
+ if (NILFS_BMAP_USE_VBN(bmap)) {
dreq->bpr_ptr = nilfs_btree_find_target_v(btree, NULL, key);
+ dat = nilfs_bmap_get_dat(bmap);
+ }
- ret = nilfs_bmap_prepare_alloc_ptr(bmap, dreq);
+ ret = nilfs_bmap_prepare_alloc_ptr(bmap, dreq, dat);
if (ret < 0)
return ret;
@@ -1679,7 +1645,7 @@ nilfs_btree_prepare_convert_and_insert(struct nilfs_bmap *bmap, __u64 key,
stats->bs_nblocks++;
if (nreq != NULL) {
nreq->bpr_ptr = dreq->bpr_ptr + 1;
- ret = nilfs_bmap_prepare_alloc_ptr(bmap, nreq);
+ ret = nilfs_bmap_prepare_alloc_ptr(bmap, nreq, dat);
if (ret < 0)
goto err_out_dreq;
@@ -1696,9 +1662,9 @@ nilfs_btree_prepare_convert_and_insert(struct nilfs_bmap *bmap, __u64 key,
/* error */
err_out_nreq:
- nilfs_bmap_abort_alloc_ptr(bmap, nreq);
+ nilfs_bmap_abort_alloc_ptr(bmap, nreq, dat);
err_out_dreq:
- nilfs_bmap_abort_alloc_ptr(bmap, dreq);
+ nilfs_bmap_abort_alloc_ptr(bmap, dreq, dat);
stats->bs_nblocks = 0;
return ret;
@@ -1713,8 +1679,9 @@ nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *bmap,
union nilfs_bmap_ptr_req *nreq,
struct buffer_head *bh)
{
- struct nilfs_btree *btree;
+ struct nilfs_btree *btree = (struct nilfs_btree *)bmap;
struct nilfs_btree_node *node;
+ struct inode *dat;
__u64 tmpptr;
/* free resources */
@@ -1725,11 +1692,11 @@ nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *bmap,
set_buffer_nilfs_volatile((struct buffer_head *)((unsigned long)ptr));
/* convert and insert */
- btree = (struct nilfs_btree *)bmap;
+ dat = NILFS_BMAP_USE_VBN(bmap) ? nilfs_bmap_get_dat(bmap) : NULL;
nilfs_btree_init(bmap);
if (nreq != NULL) {
- nilfs_bmap_commit_alloc_ptr(bmap, dreq);
- nilfs_bmap_commit_alloc_ptr(bmap, nreq);
+ nilfs_bmap_commit_alloc_ptr(bmap, dreq, dat);
+ nilfs_bmap_commit_alloc_ptr(bmap, nreq, dat);
/* create child node at level 1 */
lock_buffer(bh);
@@ -1751,7 +1718,7 @@ nilfs_btree_commit_convert_and_insert(struct nilfs_bmap *bmap,
nilfs_btree_node_init(btree, node, NILFS_BTREE_NODE_ROOT,
2, 1, &keys[0], &tmpptr);
} else {
- nilfs_bmap_commit_alloc_ptr(bmap, dreq);
+ nilfs_bmap_commit_alloc_ptr(bmap, dreq, dat);
/* create root node at level 1 */
node = nilfs_btree_get_root(btree);
@@ -1822,7 +1789,7 @@ static int nilfs_btree_propagate_p(struct nilfs_btree *btree,
static int nilfs_btree_prepare_update_v(struct nilfs_btree *btree,
struct nilfs_btree_path *path,
- int level)
+ int level, struct inode *dat)
{
struct nilfs_btree_node *parent;
int ret;
@@ -1832,9 +1799,8 @@ static int nilfs_btree_prepare_update_v(struct nilfs_btree *btree,
nilfs_btree_node_get_ptr(btree, parent,
path[level + 1].bp_index);
path[level].bp_newreq.bpr_ptr = path[level].bp_oldreq.bpr_ptr + 1;
- ret = nilfs_bmap_prepare_update_v(&btree->bt_bmap,
- &path[level].bp_oldreq,
- &path[level].bp_newreq);
+ ret = nilfs_dat_prepare_update(dat, &path[level].bp_oldreq.bpr_req,
+ &path[level].bp_newreq.bpr_req);
if (ret < 0)
return ret;
@@ -1846,9 +1812,9 @@ static int nilfs_btree_prepare_update_v(struct nilfs_btree *btree,
&NILFS_BMAP_I(&btree->bt_bmap)->i_btnode_cache,
&path[level].bp_ctxt);
if (ret < 0) {
- nilfs_bmap_abort_update_v(&btree->bt_bmap,
- &path[level].bp_oldreq,
- &path[level].bp_newreq);
+ nilfs_dat_abort_update(dat,
+ &path[level].bp_oldreq.bpr_req,
+ &path[level].bp_newreq.bpr_req);
return ret;
}
}
@@ -1858,13 +1824,13 @@ static int nilfs_btree_prepare_update_v(struct nilfs_btree *btree,
static void nilfs_btree_commit_update_v(struct nilfs_btree *btree,
struct nilfs_btree_path *path,
- int level)
+ int level, struct inode *dat)
{
struct nilfs_btree_node *parent;
- nilfs_bmap_commit_update_v(&btree->bt_bmap,
- &path[level].bp_oldreq,
- &path[level].bp_newreq);
+ nilfs_dat_commit_update(dat, &path[level].bp_oldreq.bpr_req,
+ &path[level].bp_newreq.bpr_req,
+ btree->bt_bmap.b_ptr_type == NILFS_BMAP_PTR_VS);
if (buffer_nilfs_node(path[level].bp_bh)) {
nilfs_btnode_commit_change_key(
@@ -1881,11 +1847,10 @@ static void nilfs_btree_commit_update_v(struct nilfs_btree *btree,
static void nilfs_btree_abort_update_v(struct nilfs_btree *btree,
struct nilfs_btree_path *path,
- int level)
+ int level, struct inode *dat)
{
- nilfs_bmap_abort_update_v(&btree->bt_bmap,
- &path[level].bp_oldreq,
- &path[level].bp_newreq);
+ nilfs_dat_abort_update(dat, &path[level].bp_oldreq.bpr_req,
+ &path[level].bp_newreq.bpr_req);
if (buffer_nilfs_node(path[level].bp_bh))
nilfs_btnode_abort_change_key(
&NILFS_BMAP_I(&btree->bt_bmap)->i_btnode_cache,
@@ -1894,14 +1859,14 @@ static void nilfs_btree_abort_update_v(struct nilfs_btree *btree,
static int nilfs_btree_prepare_propagate_v(struct nilfs_btree *btree,
struct nilfs_btree_path *path,
- int minlevel,
- int *maxlevelp)
+ int minlevel, int *maxlevelp,
+ struct inode *dat)
{
int level, ret;
level = minlevel;
if (!buffer_nilfs_volatile(path[level].bp_bh)) {
- ret = nilfs_btree_prepare_update_v(btree, path, level);
+ ret = nilfs_btree_prepare_update_v(btree, path, level, dat);
if (ret < 0)
return ret;
}
@@ -1909,7 +1874,7 @@ static int nilfs_btree_prepare_propagate_v(struct nilfs_btree *btree,
!buffer_dirty(path[level].bp_bh)) {
WARN_ON(buffer_nilfs_volatile(path[level].bp_bh));
- ret = nilfs_btree_prepare_update_v(btree, path, level);
+ ret = nilfs_btree_prepare_update_v(btree, path, level, dat);
if (ret < 0)
goto out;
}
@@ -1921,39 +1886,40 @@ static int nilfs_btree_prepare_propagate_v(struct nilfs_btree *btree,
/* error */
out:
while (--level > minlevel)
- nilfs_btree_abort_update_v(btree, path, level);
+ nilfs_btree_abort_update_v(btree, path, level, dat);
if (!buffer_nilfs_volatile(path[level].bp_bh))
- nilfs_btree_abort_update_v(btree, path, level);
+ nilfs_btree_abort_update_v(btree, path, level, dat);
return ret;
}
static void nilfs_btree_commit_propagate_v(struct nilfs_btree *btree,
struct nilfs_btree_path *path,
- int minlevel,
- int maxlevel,
- struct buffer_head *bh)
+ int minlevel, int maxlevel,
+ struct buffer_head *bh,
+ struct inode *dat)
{
int level;
if (!buffer_nilfs_volatile(path[minlevel].bp_bh))
- nilfs_btree_commit_update_v(btree, path, minlevel);
+ nilfs_btree_commit_update_v(btree, path, minlevel, dat);
for (level = minlevel + 1; level <= maxlevel; level++)
- nilfs_btree_commit_update_v(btree, path, level);
+ nilfs_btree_commit_update_v(btree, path, level, dat);
}
static int nilfs_btree_propagate_v(struct nilfs_btree *btree,
struct nilfs_btree_path *path,
- int level,
- struct buffer_head *bh)
+ int level, struct buffer_head *bh)
{
int maxlevel, ret;
struct nilfs_btree_node *parent;
+ struct inode *dat = nilfs_bmap_get_dat(&btree->bt_bmap);
__u64 ptr;
get_bh(bh);
path[level].bp_bh = bh;
- ret = nilfs_btree_prepare_propagate_v(btree, path, level, &maxlevel);
+ ret = nilfs_btree_prepare_propagate_v(btree, path, level, &maxlevel,
+ dat);
if (ret < 0)
goto out;
@@ -1961,12 +1927,12 @@ static int nilfs_btree_propagate_v(struct nilfs_btree *btree,
parent = nilfs_btree_get_node(btree, path, level + 1);
ptr = nilfs_btree_node_get_ptr(btree, parent,
path[level + 1].bp_index);
- ret = nilfs_bmap_mark_dirty(&btree->bt_bmap, ptr);
+ ret = nilfs_dat_mark_dirty(dat, ptr);
if (ret < 0)
goto out;
}
- nilfs_btree_commit_propagate_v(btree, path, level, maxlevel, bh);
+ nilfs_btree_commit_propagate_v(btree, path, level, maxlevel, bh, dat);
out:
brelse(path[level].bp_bh);
@@ -1986,15 +1952,15 @@ static int nilfs_btree_propagate(const struct nilfs_bmap *bmap,
WARN_ON(!buffer_dirty(bh));
btree = (struct nilfs_btree *)bmap;
- path = nilfs_btree_alloc_path(btree);
+ path = nilfs_btree_alloc_path();
if (path == NULL)
return -ENOMEM;
- nilfs_btree_init_path(btree, path);
+ nilfs_btree_init_path(path);
if (buffer_nilfs_node(bh)) {
node = (struct nilfs_btree_node *)bh->b_data;
- key = nilfs_btree_node_get_key(btree, node, 0);
- level = nilfs_btree_node_get_level(btree, node);
+ key = nilfs_btree_node_get_key(node, 0);
+ level = nilfs_btree_node_get_level(node);
} else {
key = nilfs_bmap_data_get_key(bmap, bh);
level = NILFS_BTREE_LEVEL_DATA;
@@ -2013,8 +1979,8 @@ static int nilfs_btree_propagate(const struct nilfs_bmap *bmap,
nilfs_btree_propagate_p(btree, path, level, bh);
out:
- nilfs_btree_clear_path(btree, path);
- nilfs_btree_free_path(btree, path);
+ nilfs_btree_release_path(path);
+ nilfs_btree_free_path(path);
return ret;
}
@@ -2022,7 +1988,7 @@ static int nilfs_btree_propagate(const struct nilfs_bmap *bmap,
static int nilfs_btree_propagate_gc(const struct nilfs_bmap *bmap,
struct buffer_head *bh)
{
- return nilfs_bmap_mark_dirty(bmap, bh->b_blocknr);
+ return nilfs_dat_mark_dirty(nilfs_bmap_get_dat(bmap), bh->b_blocknr);
}
static void nilfs_btree_add_dirty_buffer(struct nilfs_btree *btree,
@@ -2037,12 +2003,12 @@ static void nilfs_btree_add_dirty_buffer(struct nilfs_btree *btree,
get_bh(bh);
node = (struct nilfs_btree_node *)bh->b_data;
- key = nilfs_btree_node_get_key(btree, node, 0);
- level = nilfs_btree_node_get_level(btree, node);
+ key = nilfs_btree_node_get_key(node, 0);
+ level = nilfs_btree_node_get_level(node);
list_for_each(head, &lists[level]) {
cbh = list_entry(head, struct buffer_head, b_assoc_buffers);
cnode = (struct nilfs_btree_node *)cbh->b_data;
- ckey = nilfs_btree_node_get_key(btree, cnode, 0);
+ ckey = nilfs_btree_node_get_key(cnode, 0);
if (key < ckey)
break;
}
@@ -2120,8 +2086,7 @@ static int nilfs_btree_assign_p(struct nilfs_btree *btree,
nilfs_btree_node_set_ptr(btree, parent,
path[level + 1].bp_index, blocknr);
- key = nilfs_btree_node_get_key(btree, parent,
- path[level + 1].bp_index);
+ key = nilfs_btree_node_get_key(parent, path[level + 1].bp_index);
/* on-disk format */
binfo->bi_dat.bi_blkoff = nilfs_bmap_key_to_dkey(key);
binfo->bi_dat.bi_level = level;
@@ -2137,6 +2102,7 @@ static int nilfs_btree_assign_v(struct nilfs_btree *btree,
union nilfs_binfo *binfo)
{
struct nilfs_btree_node *parent;
+ struct inode *dat = nilfs_bmap_get_dat(&btree->bt_bmap);
__u64 key;
__u64 ptr;
union nilfs_bmap_ptr_req req;
@@ -2146,12 +2112,12 @@ static int nilfs_btree_assign_v(struct nilfs_btree *btree,
ptr = nilfs_btree_node_get_ptr(btree, parent,
path[level + 1].bp_index);
req.bpr_ptr = ptr;
- ret = nilfs_bmap_start_v(&btree->bt_bmap, &req, blocknr);
- if (unlikely(ret < 0))
+ ret = nilfs_dat_prepare_start(dat, &req.bpr_req);
+ if (ret < 0)
return ret;
+ nilfs_dat_commit_start(dat, &req.bpr_req, blocknr);
- key = nilfs_btree_node_get_key(btree, parent,
- path[level + 1].bp_index);
+ key = nilfs_btree_node_get_key(parent, path[level + 1].bp_index);
/* on-disk format */
binfo->bi_v.bi_vblocknr = nilfs_bmap_ptr_to_dptr(ptr);
binfo->bi_v.bi_blkoff = nilfs_bmap_key_to_dkey(key);
@@ -2171,15 +2137,15 @@ static int nilfs_btree_assign(struct nilfs_bmap *bmap,
int level, ret;
btree = (struct nilfs_btree *)bmap;
- path = nilfs_btree_alloc_path(btree);
+ path = nilfs_btree_alloc_path();
if (path == NULL)
return -ENOMEM;
- nilfs_btree_init_path(btree, path);
+ nilfs_btree_init_path(path);
if (buffer_nilfs_node(*bh)) {
node = (struct nilfs_btree_node *)(*bh)->b_data;
- key = nilfs_btree_node_get_key(btree, node, 0);
- level = nilfs_btree_node_get_level(btree, node);
+ key = nilfs_btree_node_get_key(node, 0);
+ level = nilfs_btree_node_get_level(node);
} else {
key = nilfs_bmap_data_get_key(bmap, *bh);
level = NILFS_BTREE_LEVEL_DATA;
@@ -2196,8 +2162,8 @@ static int nilfs_btree_assign(struct nilfs_bmap *bmap,
nilfs_btree_assign_p(btree, path, level, bh, blocknr, binfo);
out:
- nilfs_btree_clear_path(btree, path);
- nilfs_btree_free_path(btree, path);
+ nilfs_btree_release_path(path);
+ nilfs_btree_free_path(path);
return ret;
}
@@ -2207,19 +2173,18 @@ static int nilfs_btree_assign_gc(struct nilfs_bmap *bmap,
sector_t blocknr,
union nilfs_binfo *binfo)
{
- struct nilfs_btree *btree;
struct nilfs_btree_node *node;
__u64 key;
int ret;
- btree = (struct nilfs_btree *)bmap;
- ret = nilfs_bmap_move_v(bmap, (*bh)->b_blocknr, blocknr);
+ ret = nilfs_dat_move(nilfs_bmap_get_dat(bmap), (*bh)->b_blocknr,
+ blocknr);
if (ret < 0)
return ret;
if (buffer_nilfs_node(*bh)) {
node = (struct nilfs_btree_node *)(*bh)->b_data;
- key = nilfs_btree_node_get_key(btree, node, 0);
+ key = nilfs_btree_node_get_key(node, 0);
} else
key = nilfs_bmap_data_get_key(bmap, *bh);
@@ -2239,10 +2204,10 @@ static int nilfs_btree_mark(struct nilfs_bmap *bmap, __u64 key, int level)
int ret;
btree = (struct nilfs_btree *)bmap;
- path = nilfs_btree_alloc_path(btree);
+ path = nilfs_btree_alloc_path();
if (path == NULL)
return -ENOMEM;
- nilfs_btree_init_path(btree, path);
+ nilfs_btree_init_path(path);
ret = nilfs_btree_do_lookup(btree, path, key, &ptr, level + 1);
if (ret < 0) {
@@ -2262,8 +2227,8 @@ static int nilfs_btree_mark(struct nilfs_bmap *bmap, __u64 key, int level)
nilfs_bmap_set_dirty(&btree->bt_bmap);
out:
- nilfs_btree_clear_path(btree, path);
- nilfs_btree_free_path(btree, path);
+ nilfs_btree_release_path(path);
+ nilfs_btree_free_path(path);
return ret;
}
diff --git a/fs/nilfs2/cpfile.c b/fs/nilfs2/cpfile.c
index aec942cf79e..1c6cfb59128 100644
--- a/fs/nilfs2/cpfile.c
+++ b/fs/nilfs2/cpfile.c
@@ -815,8 +815,10 @@ int nilfs_cpfile_is_snapshot(struct inode *cpfile, __u64 cno)
void *kaddr;
int ret;
- if (cno == 0)
- return -ENOENT; /* checkpoint number 0 is invalid */
+ /* CP number is invalid if it's zero or larger than the
+ largest exist one.*/
+ if (cno == 0 || cno >= nilfs_mdt_cno(cpfile))
+ return -ENOENT;
down_read(&NILFS_MDT(cpfile)->mi_sem);
ret = nilfs_cpfile_get_checkpoint_block(cpfile, cno, 0, &bh);
@@ -824,7 +826,10 @@ int nilfs_cpfile_is_snapshot(struct inode *cpfile, __u64 cno)
goto out;
kaddr = kmap_atomic(bh->b_page, KM_USER0);
cp = nilfs_cpfile_block_get_checkpoint(cpfile, cno, bh, kaddr);
- ret = nilfs_checkpoint_snapshot(cp);
+ if (nilfs_checkpoint_invalid(cp))
+ ret = -ENOENT;
+ else
+ ret = nilfs_checkpoint_snapshot(cp);
kunmap_atomic(kaddr, KM_USER0);
brelse(bh);
diff --git a/fs/nilfs2/cpfile.h b/fs/nilfs2/cpfile.h
index 788a4595019..debea896e70 100644
--- a/fs/nilfs2/cpfile.h
+++ b/fs/nilfs2/cpfile.h
@@ -27,8 +27,6 @@
#include <linux/buffer_head.h>
#include <linux/nilfs2_fs.h>
-#define NILFS_CPFILE_GFP NILFS_MDT_GFP
-
int nilfs_cpfile_get_checkpoint(struct inode *, __u64, int,
struct nilfs_checkpoint **,
diff --git a/fs/nilfs2/dat.c b/fs/nilfs2/dat.c
index 8927ca27e6f..1ff8e15bd36 100644
--- a/fs/nilfs2/dat.c
+++ b/fs/nilfs2/dat.c
@@ -109,12 +109,6 @@ void nilfs_dat_commit_free(struct inode *dat, struct nilfs_palloc_req *req)
nilfs_palloc_commit_free_entry(dat, req);
}
-void nilfs_dat_abort_free(struct inode *dat, struct nilfs_palloc_req *req)
-{
- nilfs_dat_abort_entry(dat, req);
- nilfs_palloc_abort_free_entry(dat, req);
-}
-
int nilfs_dat_prepare_start(struct inode *dat, struct nilfs_palloc_req *req)
{
int ret;
@@ -140,11 +134,6 @@ void nilfs_dat_commit_start(struct inode *dat, struct nilfs_palloc_req *req,
nilfs_dat_commit_entry(dat, req);
}
-void nilfs_dat_abort_start(struct inode *dat, struct nilfs_palloc_req *req)
-{
- nilfs_dat_abort_entry(dat, req);
-}
-
int nilfs_dat_prepare_end(struct inode *dat, struct nilfs_palloc_req *req)
{
struct nilfs_dat_entry *entry;
@@ -222,6 +211,37 @@ void nilfs_dat_abort_end(struct inode *dat, struct nilfs_palloc_req *req)
nilfs_dat_abort_entry(dat, req);
}
+int nilfs_dat_prepare_update(struct inode *dat,
+ struct nilfs_palloc_req *oldreq,
+ struct nilfs_palloc_req *newreq)
+{
+ int ret;
+
+ ret = nilfs_dat_prepare_end(dat, oldreq);
+ if (!ret) {
+ ret = nilfs_dat_prepare_alloc(dat, newreq);
+ if (ret < 0)
+ nilfs_dat_abort_end(dat, oldreq);
+ }
+ return ret;
+}
+
+void nilfs_dat_commit_update(struct inode *dat,
+ struct nilfs_palloc_req *oldreq,
+ struct nilfs_palloc_req *newreq, int dead)
+{
+ nilfs_dat_commit_end(dat, oldreq, dead);
+ nilfs_dat_commit_alloc(dat, newreq);
+}
+
+void nilfs_dat_abort_update(struct inode *dat,
+ struct nilfs_palloc_req *oldreq,
+ struct nilfs_palloc_req *newreq)
+{
+ nilfs_dat_abort_end(dat, oldreq);
+ nilfs_dat_abort_alloc(dat, newreq);
+}
+
/**
* nilfs_dat_mark_dirty -
* @dat: DAT file inode
diff --git a/fs/nilfs2/dat.h b/fs/nilfs2/dat.h
index d328b81eead..406070d3ff4 100644
--- a/fs/nilfs2/dat.h
+++ b/fs/nilfs2/dat.h
@@ -27,7 +27,6 @@
#include <linux/buffer_head.h>
#include <linux/fs.h>
-#define NILFS_DAT_GFP NILFS_MDT_GFP
struct nilfs_palloc_req;
@@ -39,10 +38,15 @@ void nilfs_dat_abort_alloc(struct inode *, struct nilfs_palloc_req *);
int nilfs_dat_prepare_start(struct inode *, struct nilfs_palloc_req *);
void nilfs_dat_commit_start(struct inode *, struct nilfs_palloc_req *,
sector_t);
-void nilfs_dat_abort_start(struct inode *, struct nilfs_palloc_req *);
int nilfs_dat_prepare_end(struct inode *, struct nilfs_palloc_req *);
void nilfs_dat_commit_end(struct inode *, struct nilfs_palloc_req *, int);
void nilfs_dat_abort_end(struct inode *, struct nilfs_palloc_req *);
+int nilfs_dat_prepare_update(struct inode *, struct nilfs_palloc_req *,
+ struct nilfs_palloc_req *);
+void nilfs_dat_commit_update(struct inode *, struct nilfs_palloc_req *,
+ struct nilfs_palloc_req *, int);
+void nilfs_dat_abort_update(struct inode *, struct nilfs_palloc_req *,
+ struct nilfs_palloc_req *);
int nilfs_dat_mark_dirty(struct inode *, __u64);
int nilfs_dat_freev(struct inode *, __u64 *, size_t);
diff --git a/fs/nilfs2/direct.c b/fs/nilfs2/direct.c
index 342d9765df8..d369ac71827 100644
--- a/fs/nilfs2/direct.c
+++ b/fs/nilfs2/direct.c
@@ -125,106 +125,64 @@ static void nilfs_direct_set_target_v(struct nilfs_direct *direct,
direct->d_bmap.b_last_allocated_ptr = ptr;
}
-static int nilfs_direct_prepare_insert(struct nilfs_direct *direct,
- __u64 key,
- union nilfs_bmap_ptr_req *req,
- struct nilfs_bmap_stats *stats)
-{
- int ret;
-
- if (NILFS_BMAP_USE_VBN(&direct->d_bmap))
- req->bpr_ptr = nilfs_direct_find_target_v(direct, key);
- ret = nilfs_bmap_prepare_alloc_ptr(&direct->d_bmap, req);
- if (ret < 0)
- return ret;
-
- stats->bs_nblocks = 1;
- return 0;
-}
-
-static void nilfs_direct_commit_insert(struct nilfs_direct *direct,
- union nilfs_bmap_ptr_req *req,
- __u64 key, __u64 ptr)
-{
- struct buffer_head *bh;
-
- /* ptr must be a pointer to a buffer head. */
- bh = (struct buffer_head *)((unsigned long)ptr);
- set_buffer_nilfs_volatile(bh);
-
- nilfs_bmap_commit_alloc_ptr(&direct->d_bmap, req);
- nilfs_direct_set_ptr(direct, key, req->bpr_ptr);
-
- if (!nilfs_bmap_dirty(&direct->d_bmap))
- nilfs_bmap_set_dirty(&direct->d_bmap);
-
- if (NILFS_BMAP_USE_VBN(&direct->d_bmap))
- nilfs_direct_set_target_v(direct, key, req->bpr_ptr);
-}
-
static int nilfs_direct_insert(struct nilfs_bmap *bmap, __u64 key, __u64 ptr)
{
- struct nilfs_direct *direct;
+ struct nilfs_direct *direct = (struct nilfs_direct *)bmap;
union nilfs_bmap_ptr_req req;
- struct nilfs_bmap_stats stats;
+ struct inode *dat = NULL;
+ struct buffer_head *bh;
int ret;
- direct = (struct nilfs_direct *)bmap;
if (key > NILFS_DIRECT_KEY_MAX)
return -ENOENT;
if (nilfs_direct_get_ptr(direct, key) != NILFS_BMAP_INVALID_PTR)
return -EEXIST;
- ret = nilfs_direct_prepare_insert(direct, key, &req, &stats);
- if (ret < 0)
- return ret;
- nilfs_direct_commit_insert(direct, &req, key, ptr);
- nilfs_bmap_add_blocks(bmap, stats.bs_nblocks);
+ if (NILFS_BMAP_USE_VBN(bmap)) {
+ req.bpr_ptr = nilfs_direct_find_target_v(direct, key);
+ dat = nilfs_bmap_get_dat(bmap);
+ }
+ ret = nilfs_bmap_prepare_alloc_ptr(bmap, &req, dat);
+ if (!ret) {
+ /* ptr must be a pointer to a buffer head. */
+ bh = (struct buffer_head *)((unsigned long)ptr);
+ set_buffer_nilfs_volatile(bh);
- return 0;
-}
+ nilfs_bmap_commit_alloc_ptr(bmap, &req, dat);
+ nilfs_direct_set_ptr(direct, key, req.bpr_ptr);
-static int nilfs_direct_prepare_delete(struct nilfs_direct *direct,
- union nilfs_bmap_ptr_req *req,
- __u64 key,
- struct nilfs_bmap_stats *stats)
-{
- int ret;
+ if (!nilfs_bmap_dirty(bmap))
+ nilfs_bmap_set_dirty(bmap);
- req->bpr_ptr = nilfs_direct_get_ptr(direct, key);
- ret = nilfs_bmap_prepare_end_ptr(&direct->d_bmap, req);
- if (!ret)
- stats->bs_nblocks = 1;
- return ret;
-}
+ if (NILFS_BMAP_USE_VBN(bmap))
+ nilfs_direct_set_target_v(direct, key, req.bpr_ptr);
-static void nilfs_direct_commit_delete(struct nilfs_direct *direct,
- union nilfs_bmap_ptr_req *req,
- __u64 key)
-{
- nilfs_bmap_commit_end_ptr(&direct->d_bmap, req);
- nilfs_direct_set_ptr(direct, key, NILFS_BMAP_INVALID_PTR);
+ nilfs_bmap_add_blocks(bmap, 1);
+ }
+ return ret;
}
static int nilfs_direct_delete(struct nilfs_bmap *bmap, __u64 key)
{
- struct nilfs_direct *direct;
+ struct nilfs_direct *direct = (struct nilfs_direct *)bmap;
union nilfs_bmap_ptr_req req;
- struct nilfs_bmap_stats stats;
+ struct inode *dat;
int ret;
- direct = (struct nilfs_direct *)bmap;
- if ((key > NILFS_DIRECT_KEY_MAX) ||
+ if (key > NILFS_DIRECT_KEY_MAX ||
nilfs_direct_get_ptr(direct, key) == NILFS_BMAP_INVALID_PTR)
return -ENOENT;
- ret = nilfs_direct_prepare_delete(direct, &req, key, &stats);
- if (ret < 0)
- return ret;
- nilfs_direct_commit_delete(direct, &req, key);
- nilfs_bmap_sub_blocks(bmap, stats.bs_nblocks);
+ dat = NILFS_BMAP_USE_VBN(bmap) ? nilfs_bmap_get_dat(bmap) : NULL;
+ req.bpr_ptr = nilfs_direct_get_ptr(direct, key);
- return 0;
+ ret = nilfs_bmap_prepare_end_ptr(bmap, &req, dat);
+ if (!ret) {
+ nilfs_bmap_commit_end_ptr(bmap, &req, dat);
+ nilfs_direct_set_ptr(direct, key, NILFS_BMAP_INVALID_PTR);
+ nilfs_bmap_sub_blocks(bmap, 1);
+ }
+ return ret;
}
static int nilfs_direct_last_key(const struct nilfs_bmap *bmap, __u64 *keyp)
@@ -310,59 +268,56 @@ int nilfs_direct_delete_and_convert(struct nilfs_bmap *bmap,
return 0;
}
-static int nilfs_direct_propagate_v(struct nilfs_direct *direct,
- struct buffer_head *bh)
+static int nilfs_direct_propagate(const struct nilfs_bmap *bmap,
+ struct buffer_head *bh)
{
- union nilfs_bmap_ptr_req oldreq, newreq;
+ struct nilfs_direct *direct = (struct nilfs_direct *)bmap;
+ struct nilfs_palloc_req oldreq, newreq;
+ struct inode *dat;
__u64 key;
__u64 ptr;
int ret;
- key = nilfs_bmap_data_get_key(&direct->d_bmap, bh);
+ if (!NILFS_BMAP_USE_VBN(bmap))
+ return 0;
+
+ dat = nilfs_bmap_get_dat(bmap);
+ key = nilfs_bmap_data_get_key(bmap, bh);
ptr = nilfs_direct_get_ptr(direct, key);
if (!buffer_nilfs_volatile(bh)) {
- oldreq.bpr_ptr = ptr;
- newreq.bpr_ptr = ptr;
- ret = nilfs_bmap_prepare_update_v(&direct->d_bmap, &oldreq,
- &newreq);
+ oldreq.pr_entry_nr = ptr;
+ newreq.pr_entry_nr = ptr;
+ ret = nilfs_dat_prepare_update(dat, &oldreq, &newreq);
if (ret < 0)
return ret;
- nilfs_bmap_commit_update_v(&direct->d_bmap, &oldreq, &newreq);
+ nilfs_dat_commit_update(dat, &oldreq, &newreq,
+ bmap->b_ptr_type == NILFS_BMAP_PTR_VS);
set_buffer_nilfs_volatile(bh);
- nilfs_direct_set_ptr(direct, key, newreq.bpr_ptr);
+ nilfs_direct_set_ptr(direct, key, newreq.pr_entry_nr);
} else
- ret = nilfs_bmap_mark_dirty(&direct->d_bmap, ptr);
+ ret = nilfs_dat_mark_dirty(dat, ptr);
return ret;
}
-static int nilfs_direct_propagate(const struct nilfs_bmap *bmap,
- struct buffer_head *bh)
-{
- struct nilfs_direct *direct = (struct nilfs_direct *)bmap;
-
- return NILFS_BMAP_USE_VBN(bmap) ?
- nilfs_direct_propagate_v(direct, bh) : 0;
-}
-
static int nilfs_direct_assign_v(struct nilfs_direct *direct,
__u64 key, __u64 ptr,
struct buffer_head **bh,
sector_t blocknr,
union nilfs_binfo *binfo)
{
+ struct inode *dat = nilfs_bmap_get_dat(&direct->d_bmap);
union nilfs_bmap_ptr_req req;
int ret;
req.bpr_ptr = ptr;
- ret = nilfs_bmap_start_v(&direct->d_bmap, &req, blocknr);
- if (unlikely(ret < 0))
- return ret;
-
- binfo->bi_v.bi_vblocknr = nilfs_bmap_ptr_to_dptr(ptr);
- binfo->bi_v.bi_blkoff = nilfs_bmap_key_to_dkey(key);
-
- return 0;
+ ret = nilfs_dat_prepare_start(dat, &req.bpr_req);
+ if (!ret) {
+ nilfs_dat_commit_start(dat, &req.bpr_req, blocknr);
+ binfo->bi_v.bi_vblocknr = nilfs_bmap_ptr_to_dptr(ptr);
+ binfo->bi_v.bi_blkoff = nilfs_bmap_key_to_dkey(key);
+ }
+ return ret;
}
static int nilfs_direct_assign_p(struct nilfs_direct *direct,
diff --git a/fs/nilfs2/ifile.h b/fs/nilfs2/ifile.h
index 5d30a35679b..ecc3ba76db4 100644
--- a/fs/nilfs2/ifile.h
+++ b/fs/nilfs2/ifile.h
@@ -31,7 +31,6 @@
#include "mdt.h"
#include "alloc.h"
-#define NILFS_IFILE_GFP NILFS_MDT_GFP
static inline struct nilfs_inode *
nilfs_ifile_map_inode(struct inode *ifile, ino_t ino, struct buffer_head *ibh)
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index fe9d8f2a13f..807e584b163 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -430,7 +430,8 @@ static int __nilfs_read_inode(struct super_block *sb, unsigned long ino,
raw_inode = nilfs_ifile_map_inode(sbi->s_ifile, ino, bh);
- if (nilfs_read_inode_common(inode, raw_inode))
+ err = nilfs_read_inode_common(inode, raw_inode);
+ if (err)
goto failed_unmap;
if (S_ISREG(inode->i_mode)) {
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 6ea5f872e2d..6572ea4bc4d 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -442,12 +442,6 @@ int nilfs_ioctl_prepare_clean_segments(struct the_nilfs *nilfs,
const char *msg;
int ret;
- ret = nilfs_ioctl_move_blocks(nilfs, &argv[0], kbufs[0]);
- if (ret < 0) {
- msg = "cannot read source blocks";
- goto failed;
- }
-
ret = nilfs_ioctl_delete_checkpoints(nilfs, &argv[1], kbufs[1]);
if (ret < 0) {
/*
@@ -548,7 +542,25 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp,
}
}
- ret = nilfs_clean_segments(inode->i_sb, argv, kbufs);
+ /*
+ * nilfs_ioctl_move_blocks() will call nilfs_gc_iget(),
+ * which will operates an inode list without blocking.
+ * To protect the list from concurrent operations,
+ * nilfs_ioctl_move_blocks should be atomic operation.
+ */
+ if (test_and_set_bit(THE_NILFS_GC_RUNNING, &nilfs->ns_flags)) {
+ ret = -EBUSY;
+ goto out_free;
+ }
+
+ ret = nilfs_ioctl_move_blocks(nilfs, &argv[0], kbufs[0]);
+ if (ret < 0)
+ printk(KERN_ERR "NILFS: GC failed during preparation: "
+ "cannot read source blocks: err=%d\n", ret);
+ else
+ ret = nilfs_clean_segments(inode->i_sb, argv, kbufs);
+
+ clear_nilfs_gc_running(nilfs);
out_free:
while (--n >= 0)
diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c
index 2dfd47714ae..156bf6091a9 100644
--- a/fs/nilfs2/mdt.c
+++ b/fs/nilfs2/mdt.c
@@ -103,15 +103,12 @@ static int nilfs_mdt_create_block(struct inode *inode, unsigned long block,
goto failed_unlock;
err = -EEXIST;
- if (buffer_uptodate(bh) || buffer_mapped(bh))
+ if (buffer_uptodate(bh))
goto failed_bh;
-#if 0
- /* The uptodate flag is not protected by the page lock, but
- the mapped flag is. Thus, we don't have to wait the buffer. */
+
wait_on_buffer(bh);
if (buffer_uptodate(bh))
goto failed_bh;
-#endif
bh->b_bdev = nilfs->ns_bdev;
err = nilfs_mdt_insert_new_block(inode, block, bh, init_block);
@@ -139,7 +136,7 @@ nilfs_mdt_submit_block(struct inode *inode, unsigned long blkoff,
int mode, struct buffer_head **out_bh)
{
struct buffer_head *bh;
- unsigned long blknum = 0;
+ __u64 blknum = 0;
int ret = -ENOMEM;
bh = nilfs_grab_buffer(inode, inode->i_mapping, blkoff, 0);
@@ -162,17 +159,15 @@ nilfs_mdt_submit_block(struct inode *inode, unsigned long blkoff,
unlock_buffer(bh);
goto out;
}
- if (!buffer_mapped(bh)) { /* unused buffer */
- ret = nilfs_bmap_lookup(NILFS_I(inode)->i_bmap, blkoff,
- &blknum);
- if (unlikely(ret)) {
- unlock_buffer(bh);
- goto failed_bh;
- }
- bh->b_bdev = NILFS_MDT(inode)->mi_nilfs->ns_bdev;
- bh->b_blocknr = blknum;
- set_buffer_mapped(bh);
+
+ ret = nilfs_bmap_lookup(NILFS_I(inode)->i_bmap, blkoff, &blknum);
+ if (unlikely(ret)) {
+ unlock_buffer(bh);
+ goto failed_bh;
}
+ bh->b_bdev = NILFS_MDT(inode)->mi_nilfs->ns_bdev;
+ bh->b_blocknr = (sector_t)blknum;
+ set_buffer_mapped(bh);
bh->b_end_io = end_buffer_read_sync;
get_bh(bh);
@@ -402,6 +397,7 @@ nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc)
struct inode *inode = container_of(page->mapping,
struct inode, i_data);
struct super_block *sb = inode->i_sb;
+ struct the_nilfs *nilfs = NILFS_MDT(inode)->mi_nilfs;
struct nilfs_sb_info *writer = NULL;
int err = 0;
@@ -411,9 +407,10 @@ nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc)
if (page->mapping->assoc_mapping)
return 0; /* Do not request flush for shadow page cache */
if (!sb) {
- writer = nilfs_get_writer(NILFS_MDT(inode)->mi_nilfs);
+ down_read(&nilfs->ns_writer_sem);
+ writer = nilfs->ns_writer;
if (!writer) {
- nilfs_put_writer(NILFS_MDT(inode)->mi_nilfs);
+ up_read(&nilfs->ns_writer_sem);
return -EROFS;
}
sb = writer->s_super;
@@ -425,7 +422,7 @@ nilfs_mdt_write_page(struct page *page, struct writeback_control *wbc)
nilfs_flush_segment(sb, inode->i_ino);
if (writer)
- nilfs_put_writer(NILFS_MDT(inode)->mi_nilfs);
+ up_read(&nilfs->ns_writer_sem);
return err;
}
@@ -516,9 +513,10 @@ nilfs_mdt_new_common(struct the_nilfs *nilfs, struct super_block *sb,
}
struct inode *nilfs_mdt_new(struct the_nilfs *nilfs, struct super_block *sb,
- ino_t ino, gfp_t gfp_mask)
+ ino_t ino)
{
- struct inode *inode = nilfs_mdt_new_common(nilfs, sb, ino, gfp_mask);
+ struct inode *inode = nilfs_mdt_new_common(nilfs, sb, ino,
+ NILFS_MDT_GFP);
if (!inode)
return NULL;
diff --git a/fs/nilfs2/mdt.h b/fs/nilfs2/mdt.h
index df683e0bca6..431599733c9 100644
--- a/fs/nilfs2/mdt.h
+++ b/fs/nilfs2/mdt.h
@@ -74,8 +74,7 @@ int nilfs_mdt_forget_block(struct inode *, unsigned long);
int nilfs_mdt_mark_block_dirty(struct inode *, unsigned long);
int nilfs_mdt_fetch_dirty(struct inode *);
-struct inode *nilfs_mdt_new(struct the_nilfs *, struct super_block *, ino_t,
- gfp_t);
+struct inode *nilfs_mdt_new(struct the_nilfs *, struct super_block *, ino_t);
struct inode *nilfs_mdt_new_common(struct the_nilfs *, struct super_block *,
ino_t, gfp_t);
void nilfs_mdt_destroy(struct inode *);
diff --git a/fs/nilfs2/recovery.c b/fs/nilfs2/recovery.c
index d80cc71be74..6dc83591d11 100644
--- a/fs/nilfs2/recovery.c
+++ b/fs/nilfs2/recovery.c
@@ -552,7 +552,8 @@ static int recover_dsync_blocks(struct nilfs_sb_info *sbi,
printk(KERN_WARNING
"NILFS warning: error recovering data block "
"(err=%d, ino=%lu, block-offset=%llu)\n",
- err, rb->ino, (unsigned long long)rb->blkoff);
+ err, (unsigned long)rb->ino,
+ (unsigned long long)rb->blkoff);
if (!err2)
err2 = err;
next:
diff --git a/fs/nilfs2/segbuf.c b/fs/nilfs2/segbuf.c
index 9e3fe17bb96..e6d9e37fa24 100644
--- a/fs/nilfs2/segbuf.c
+++ b/fs/nilfs2/segbuf.c
@@ -316,10 +316,10 @@ static struct bio *nilfs_alloc_seg_bio(struct super_block *sb, sector_t start,
{
struct bio *bio;
- bio = bio_alloc(GFP_NOWAIT, nr_vecs);
+ bio = bio_alloc(GFP_NOIO, nr_vecs);
if (bio == NULL) {
while (!bio && (nr_vecs >>= 1))
- bio = bio_alloc(GFP_NOWAIT, nr_vecs);
+ bio = bio_alloc(GFP_NOIO, nr_vecs);
}
if (likely(bio)) {
bio->bi_bdev = sb->s_bdev;
diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c
index 51ff3d0a4ee..683df89dbae 100644
--- a/fs/nilfs2/segment.c
+++ b/fs/nilfs2/segment.c
@@ -2501,7 +2501,8 @@ static int nilfs_segctor_construct(struct nilfs_sc_info *sci,
if (test_bit(NILFS_SC_SUPER_ROOT, &sci->sc_flags) &&
nilfs_discontinued(nilfs)) {
down_write(&nilfs->ns_sem);
- req->sb_err = nilfs_commit_super(sbi, 0);
+ req->sb_err = nilfs_commit_super(sbi,
+ nilfs_altsb_need_update(nilfs));
up_write(&nilfs->ns_sem);
}
}
@@ -2689,6 +2690,7 @@ static int nilfs_segctor_thread(void *arg)
} else {
DEFINE_WAIT(wait);
int should_sleep = 1;
+ struct the_nilfs *nilfs;
prepare_to_wait(&sci->sc_wait_daemon, &wait,
TASK_INTERRUPTIBLE);
@@ -2709,6 +2711,9 @@ static int nilfs_segctor_thread(void *arg)
finish_wait(&sci->sc_wait_daemon, &wait);
timeout = ((sci->sc_state & NILFS_SEGCTOR_COMMIT) &&
time_after_eq(jiffies, sci->sc_timer->expires));
+ nilfs = sci->sc_sbi->s_nilfs;
+ if (sci->sc_super->s_dirt && nilfs_sb_need_update(nilfs))
+ set_nilfs_discontinued(nilfs);
}
goto loop;
diff --git a/fs/nilfs2/sufile.h b/fs/nilfs2/sufile.h
index a2c4d76c336..0e99e5c0bd0 100644
--- a/fs/nilfs2/sufile.h
+++ b/fs/nilfs2/sufile.h
@@ -28,7 +28,6 @@
#include <linux/nilfs2_fs.h>
#include "mdt.h"
-#define NILFS_SUFILE_GFP NILFS_MDT_GFP
static inline unsigned long nilfs_sufile_get_nsegments(struct inode *sufile)
{
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 151964f0de4..55f3d6b6073 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -50,6 +50,8 @@
#include <linux/writeback.h>
#include <linux/kobject.h>
#include <linux/exportfs.h>
+#include <linux/seq_file.h>
+#include <linux/mount.h>
#include "nilfs.h"
#include "mdt.h"
#include "alloc.h"
@@ -65,7 +67,6 @@ MODULE_DESCRIPTION("A New Implementation of the Log-structured Filesystem "
"(NILFS)");
MODULE_LICENSE("GPL");
-static void nilfs_write_super(struct super_block *sb);
static int nilfs_remount(struct super_block *sb, int *flags, char *data);
/**
@@ -311,9 +312,6 @@ static void nilfs_put_super(struct super_block *sb)
lock_kernel();
- if (sb->s_dirt)
- nilfs_write_super(sb);
-
nilfs_detach_segment_constructor(sbi);
if (!(sb->s_flags & MS_RDONLY)) {
@@ -336,63 +334,21 @@ static void nilfs_put_super(struct super_block *sb)
unlock_kernel();
}
-/**
- * nilfs_write_super - write super block(s) of NILFS
- * @sb: super_block
- *
- * nilfs_write_super() gets a fs-dependent lock, writes super block(s), and
- * clears s_dirt. This function is called in the section protected by
- * lock_super().
- *
- * The s_dirt flag is managed by each filesystem and we protect it by ns_sem
- * of the struct the_nilfs. Lock order must be as follows:
- *
- * 1. lock_super()
- * 2. down_write(&nilfs->ns_sem)
- *
- * Inside NILFS, locking ns_sem is enough to protect s_dirt and the buffer
- * of the super block (nilfs->ns_sbp[]).
- *
- * In most cases, VFS functions call lock_super() before calling these
- * methods. So we must be careful not to bring on deadlocks when using
- * lock_super(); see generic_shutdown_super(), write_super(), and so on.
- *
- * Note that order of lock_kernel() and lock_super() depends on contexts
- * of VFS. We should also note that lock_kernel() can be used in its
- * protective section and only the outermost one has an effect.
- */
-static void nilfs_write_super(struct super_block *sb)
+static int nilfs_sync_fs(struct super_block *sb, int wait)
{
struct nilfs_sb_info *sbi = NILFS_SB(sb);
struct the_nilfs *nilfs = sbi->s_nilfs;
-
- down_write(&nilfs->ns_sem);
- if (!(sb->s_flags & MS_RDONLY)) {
- struct nilfs_super_block **sbp = nilfs->ns_sbp;
- u64 t = get_seconds();
- int dupsb;
-
- if (!nilfs_discontinued(nilfs) && t >= nilfs->ns_sbwtime[0] &&
- t < nilfs->ns_sbwtime[0] + NILFS_SB_FREQ) {
- up_write(&nilfs->ns_sem);
- return;
- }
- dupsb = sbp[1] && t > nilfs->ns_sbwtime[1] + NILFS_ALTSB_FREQ;
- nilfs_commit_super(sbi, dupsb);
- }
- sb->s_dirt = 0;
- up_write(&nilfs->ns_sem);
-}
-
-static int nilfs_sync_fs(struct super_block *sb, int wait)
-{
int err = 0;
- nilfs_write_super(sb);
-
/* This function is called when super block should be written back */
if (wait)
err = nilfs_construct_segment(sb);
+
+ down_write(&nilfs->ns_sem);
+ if (sb->s_dirt)
+ nilfs_commit_super(sbi, 1);
+ up_write(&nilfs->ns_sem);
+
return err;
}
@@ -407,8 +363,7 @@ int nilfs_attach_checkpoint(struct nilfs_sb_info *sbi, __u64 cno)
list_add(&sbi->s_list, &nilfs->ns_supers);
up_write(&nilfs->ns_super_sem);
- sbi->s_ifile = nilfs_mdt_new(
- nilfs, sbi->s_super, NILFS_IFILE_INO, NILFS_IFILE_GFP);
+ sbi->s_ifile = nilfs_mdt_new(nilfs, sbi->s_super, NILFS_IFILE_INO);
if (!sbi->s_ifile)
return -ENOMEM;
@@ -529,6 +484,26 @@ static int nilfs_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0;
}
+static int nilfs_show_options(struct seq_file *seq, struct vfsmount *vfs)
+{
+ struct super_block *sb = vfs->mnt_sb;
+ struct nilfs_sb_info *sbi = NILFS_SB(sb);
+
+ if (!nilfs_test_opt(sbi, BARRIER))
+ seq_printf(seq, ",barrier=off");
+ if (nilfs_test_opt(sbi, SNAPSHOT))
+ seq_printf(seq, ",cp=%llu",
+ (unsigned long long int)sbi->s_snapshot_cno);
+ if (nilfs_test_opt(sbi, ERRORS_RO))
+ seq_printf(seq, ",errors=remount-ro");
+ if (nilfs_test_opt(sbi, ERRORS_PANIC))
+ seq_printf(seq, ",errors=panic");
+ if (nilfs_test_opt(sbi, STRICT_ORDER))
+ seq_printf(seq, ",order=strict");
+
+ return 0;
+}
+
static struct super_operations nilfs_sops = {
.alloc_inode = nilfs_alloc_inode,
.destroy_inode = nilfs_destroy_inode,
@@ -538,7 +513,7 @@ static struct super_operations nilfs_sops = {
/* .drop_inode = nilfs_drop_inode, */
.delete_inode = nilfs_delete_inode,
.put_super = nilfs_put_super,
- .write_super = nilfs_write_super,
+ /* .write_super = nilfs_write_super, */
.sync_fs = nilfs_sync_fs,
/* .write_super_lockfs */
/* .unlockfs */
@@ -546,7 +521,7 @@ static struct super_operations nilfs_sops = {
.remount_fs = nilfs_remount,
.clear_inode = nilfs_clear_inode,
/* .umount_begin */
- /* .show_options */
+ .show_options = nilfs_show_options
};
static struct inode *
@@ -816,10 +791,15 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent,
if (sb->s_flags & MS_RDONLY) {
if (nilfs_test_opt(sbi, SNAPSHOT)) {
+ down_read(&nilfs->ns_segctor_sem);
err = nilfs_cpfile_is_snapshot(nilfs->ns_cpfile,
sbi->s_snapshot_cno);
- if (err < 0)
+ up_read(&nilfs->ns_segctor_sem);
+ if (err < 0) {
+ if (err == -ENOENT)
+ err = -EINVAL;
goto failed_sbi;
+ }
if (!err) {
printk(KERN_ERR
"NILFS: The specified checkpoint is "
@@ -1127,10 +1107,6 @@ nilfs_get_sb(struct file_system_type *fs_type, int flags,
*/
sd.sbi = nilfs_find_sbinfo(nilfs, !(flags & MS_RDONLY), sd.cno);
- if (!sd.cno)
- /* trying to get the latest checkpoint. */
- sd.cno = nilfs_last_cno(nilfs);
-
/*
* Get super block instance holding the nilfs_sb_info struct.
* A new instance is allocated if no existing mount is present or
diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c
index 8b888982571..d4168e269c5 100644
--- a/fs/nilfs2/the_nilfs.c
+++ b/fs/nilfs2/the_nilfs.c
@@ -68,12 +68,11 @@ static struct the_nilfs *alloc_nilfs(struct block_device *bdev)
nilfs->ns_bdev = bdev;
atomic_set(&nilfs->ns_count, 1);
- atomic_set(&nilfs->ns_writer_refcount, -1);
atomic_set(&nilfs->ns_ndirtyblks, 0);
init_rwsem(&nilfs->ns_sem);
init_rwsem(&nilfs->ns_super_sem);
mutex_init(&nilfs->ns_mount_mutex);
- mutex_init(&nilfs->ns_writer_mutex);
+ init_rwsem(&nilfs->ns_writer_sem);
INIT_LIST_HEAD(&nilfs->ns_list);
INIT_LIST_HEAD(&nilfs->ns_supers);
spin_lock_init(&nilfs->ns_last_segment_lock);
@@ -188,23 +187,19 @@ static int nilfs_load_super_root(struct the_nilfs *nilfs,
inode_size = nilfs->ns_inode_size;
err = -ENOMEM;
- nilfs->ns_dat = nilfs_mdt_new(
- nilfs, NULL, NILFS_DAT_INO, NILFS_DAT_GFP);
+ nilfs->ns_dat = nilfs_mdt_new(nilfs, NULL, NILFS_DAT_INO);
if (unlikely(!nilfs->ns_dat))
goto failed;
- nilfs->ns_gc_dat = nilfs_mdt_new(
- nilfs, NULL, NILFS_DAT_INO, NILFS_DAT_GFP);
+ nilfs->ns_gc_dat = nilfs_mdt_new(nilfs, NULL, NILFS_DAT_INO);
if (unlikely(!nilfs->ns_gc_dat))
goto failed_dat;
- nilfs->ns_cpfile = nilfs_mdt_new(
- nilfs, NULL, NILFS_CPFILE_INO, NILFS_CPFILE_GFP);
+ nilfs->ns_cpfile = nilfs_mdt_new(nilfs, NULL, NILFS_CPFILE_INO);
if (unlikely(!nilfs->ns_cpfile))
goto failed_gc_dat;
- nilfs->ns_sufile = nilfs_mdt_new(
- nilfs, NULL, NILFS_SUFILE_INO, NILFS_SUFILE_GFP);
+ nilfs->ns_sufile = nilfs_mdt_new(nilfs, NULL, NILFS_SUFILE_INO);
if (unlikely(!nilfs->ns_sufile))
goto failed_cpfile;
diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h
index 1b9caafb866..20abd55881e 100644
--- a/fs/nilfs2/the_nilfs.h
+++ b/fs/nilfs2/the_nilfs.h
@@ -37,6 +37,7 @@ enum {
THE_NILFS_LOADED, /* Roll-back/roll-forward has done and
the latest checkpoint was loaded */
THE_NILFS_DISCONTINUED, /* 'next' pointer chain has broken */
+ THE_NILFS_GC_RUNNING, /* gc process is running */
};
/**
@@ -50,8 +51,7 @@ enum {
* @ns_sem: semaphore for shared states
* @ns_super_sem: semaphore for global operations across super block instances
* @ns_mount_mutex: mutex protecting mount process of nilfs
- * @ns_writer_mutex: mutex protecting ns_writer attach/detach
- * @ns_writer_refcount: number of referrers on ns_writer
+ * @ns_writer_sem: semaphore protecting ns_writer attach/detach
* @ns_current: back pointer to current mount
* @ns_sbh: buffer heads of on-disk super blocks
* @ns_sbp: pointers to super block data
@@ -100,8 +100,7 @@ struct the_nilfs {
struct rw_semaphore ns_sem;
struct rw_semaphore ns_super_sem;
struct mutex ns_mount_mutex;
- struct mutex ns_writer_mutex;
- atomic_t ns_writer_refcount;
+ struct rw_semaphore ns_writer_sem;
/*
* components protected by ns_super_sem
@@ -197,11 +196,26 @@ static inline int nilfs_##name(struct the_nilfs *nilfs) \
THE_NILFS_FNS(INIT, init)
THE_NILFS_FNS(LOADED, loaded)
THE_NILFS_FNS(DISCONTINUED, discontinued)
+THE_NILFS_FNS(GC_RUNNING, gc_running)
/* Minimum interval of periodical update of superblocks (in seconds) */
#define NILFS_SB_FREQ 10
#define NILFS_ALTSB_FREQ 60 /* spare superblock */
+static inline int nilfs_sb_need_update(struct the_nilfs *nilfs)
+{
+ u64 t = get_seconds();
+ return t < nilfs->ns_sbwtime[0] ||
+ t > nilfs->ns_sbwtime[0] + NILFS_SB_FREQ;
+}
+
+static inline int nilfs_altsb_need_update(struct the_nilfs *nilfs)
+{
+ u64 t = get_seconds();
+ struct nilfs_super_block **sbp = nilfs->ns_sbp;
+ return sbp[1] && t > nilfs->ns_sbwtime[1] + NILFS_ALTSB_FREQ;
+}
+
void nilfs_set_last_segment(struct the_nilfs *, sector_t, u64, __u64);
struct the_nilfs *find_or_create_nilfs(struct block_device *);
void put_nilfs(struct the_nilfs *);
@@ -221,34 +235,21 @@ static inline void get_nilfs(struct the_nilfs *nilfs)
atomic_inc(&nilfs->ns_count);
}
-static inline struct nilfs_sb_info *nilfs_get_writer(struct the_nilfs *nilfs)
-{
- if (atomic_inc_and_test(&nilfs->ns_writer_refcount))
- mutex_lock(&nilfs->ns_writer_mutex);
- return nilfs->ns_writer;
-}
-
-static inline void nilfs_put_writer(struct the_nilfs *nilfs)
-{
- if (atomic_add_negative(-1, &nilfs->ns_writer_refcount))
- mutex_unlock(&nilfs->ns_writer_mutex);
-}
-
static inline void
nilfs_attach_writer(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi)
{
- mutex_lock(&nilfs->ns_writer_mutex);
+ down_write(&nilfs->ns_writer_sem);
nilfs->ns_writer = sbi;
- mutex_unlock(&nilfs->ns_writer_mutex);
+ up_write(&nilfs->ns_writer_sem);
}
static inline void
nilfs_detach_writer(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi)
{
- mutex_lock(&nilfs->ns_writer_mutex);
+ down_write(&nilfs->ns_writer_sem);
if (sbi == nilfs->ns_writer)
nilfs->ns_writer = NULL;
- mutex_unlock(&nilfs->ns_writer_mutex);
+ up_write(&nilfs->ns_writer_sem);
}
static inline void nilfs_put_sbinfo(struct nilfs_sb_info *sbi)
diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c
index 3140a4429af..4350d4993b1 100644
--- a/fs/ntfs/file.c
+++ b/fs/ntfs/file.c
@@ -2076,14 +2076,6 @@ err_out:
*ppos = pos;
if (cached_page)
page_cache_release(cached_page);
- /* For now, when the user asks for O_SYNC, we actually give O_DSYNC. */
- if (likely(!status)) {
- if (unlikely((file->f_flags & O_SYNC) || IS_SYNC(vi))) {
- if (!mapping->a_ops->writepage || !is_sync_kiocb(iocb))
- status = generic_osync_inode(vi, mapping,
- OSYNC_METADATA|OSYNC_DATA);
- }
- }
pagevec_lru_add_file(&lru_pvec);
ntfs_debug("Done. Returning %s (written 0x%lx, status %li).",
written ? "written" : "status", (unsigned long)written,
@@ -2145,8 +2137,8 @@ static ssize_t ntfs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
mutex_lock(&inode->i_mutex);
ret = ntfs_file_aio_write_nolock(iocb, iov, nr_segs, &iocb->ki_pos);
mutex_unlock(&inode->i_mutex);
- if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
- int err = sync_page_range(inode, mapping, pos, ret);
+ if (ret > 0) {
+ int err = generic_write_sync(file, pos, ret);
if (err < 0)
ret = err;
}
@@ -2173,8 +2165,8 @@ static ssize_t ntfs_file_writev(struct file *file, const struct iovec *iov,
if (ret == -EIOCBQUEUED)
ret = wait_on_sync_kiocb(&kiocb);
mutex_unlock(&inode->i_mutex);
- if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
- int err = sync_page_range(inode, mapping, *ppos - ret, ret);
+ if (ret > 0) {
+ int err = generic_write_sync(file, *ppos - ret, ret);
if (err < 0)
ret = err;
}
diff --git a/fs/ntfs/mft.c b/fs/ntfs/mft.c
index 23bf68453d7..1caa0ef0b2b 100644
--- a/fs/ntfs/mft.c
+++ b/fs/ntfs/mft.c
@@ -384,13 +384,12 @@ unm_err_out:
* it is dirty in the inode meta data rather than the data page cache of the
* inode, and thus there are no data pages that need writing out. Therefore, a
* full mark_inode_dirty() is overkill. A mark_inode_dirty_sync(), on the
- * other hand, is not sufficient, because I_DIRTY_DATASYNC needs to be set to
- * ensure ->write_inode is called from generic_osync_inode() and this needs to
- * happen or the file data would not necessarily hit the device synchronously,
- * even though the vfs inode has the O_SYNC flag set. Also, I_DIRTY_DATASYNC
- * simply "feels" better than just I_DIRTY_SYNC, since the file data has not
- * actually hit the block device yet, which is not what I_DIRTY_SYNC on its own
- * would suggest.
+ * other hand, is not sufficient, because ->write_inode needs to be called even
+ * in case of fdatasync. This needs to happen or the file data would not
+ * necessarily hit the device synchronously, even though the vfs inode has the
+ * O_SYNC flag set. Also, I_DIRTY_DATASYNC simply "feels" better than just
+ * I_DIRTY_SYNC, since the file data has not actually hit the block device yet,
+ * which is not what I_DIRTY_SYNC on its own would suggest.
*/
void __mark_mft_record_dirty(ntfs_inode *ni)
{
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index aa501d3f93f..221c5e98957 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1871,8 +1871,7 @@ relock:
goto out_dio;
}
} else {
- written = generic_file_aio_write_nolock(iocb, iov, nr_segs,
- *ppos);
+ written = __generic_file_aio_write(iocb, iov, nr_segs, ppos);
}
out_dio:
@@ -1880,18 +1879,21 @@ out_dio:
BUG_ON(ret == -EIOCBQUEUED && !(file->f_flags & O_DIRECT));
if ((file->f_flags & O_SYNC && !direct_io) || IS_SYNC(inode)) {
- /*
- * The generic write paths have handled getting data
- * to disk, but since we don't make use of the dirty
- * inode list, a manual journal commit is necessary
- * here.
- */
- if (old_size != i_size_read(inode) ||
- old_clusters != OCFS2_I(inode)->ip_clusters) {
+ ret = filemap_fdatawrite_range(file->f_mapping, pos,
+ pos + count - 1);
+ if (ret < 0)
+ written = ret;
+
+ if (!ret && (old_size != i_size_read(inode) ||
+ old_clusters != OCFS2_I(inode)->ip_clusters)) {
ret = jbd2_journal_force_commit(osb->journal->j_journal);
if (ret < 0)
written = ret;
}
+
+ if (!ret)
+ ret = filemap_fdatawait_range(file->f_mapping, pos,
+ pos + count - 1);
}
/*
@@ -1991,31 +1993,16 @@ static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe,
if (ret > 0) {
unsigned long nr_pages;
+ int err;
- *ppos += ret;
nr_pages = (ret + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
- /*
- * If file or inode is SYNC and we actually wrote some data,
- * sync it.
- */
- if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(inode))) {
- int err;
-
- mutex_lock(&inode->i_mutex);
- err = ocfs2_rw_lock(inode, 1);
- if (err < 0) {
- mlog_errno(err);
- } else {
- err = generic_osync_inode(inode, mapping,
- OSYNC_METADATA|OSYNC_DATA);
- ocfs2_rw_unlock(inode, 1);
- }
- mutex_unlock(&inode->i_mutex);
+ err = generic_write_sync(out, *ppos, ret);
+ if (err)
+ ret = err;
+ else
+ *ppos += ret;
- if (err)
- ret = err;
- }
balance_dirty_pages_ratelimited_nr(mapping, nr_pages);
}
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index ea4e6cb29e1..619ba99dfe3 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -248,11 +248,19 @@ ssize_t part_stat_show(struct device *dev,
part_stat_read(p, merges[WRITE]),
(unsigned long long)part_stat_read(p, sectors[WRITE]),
jiffies_to_msecs(part_stat_read(p, ticks[WRITE])),
- p->in_flight,
+ part_in_flight(p),
jiffies_to_msecs(part_stat_read(p, io_ticks)),
jiffies_to_msecs(part_stat_read(p, time_in_queue)));
}
+ssize_t part_inflight_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct hd_struct *p = dev_to_part(dev);
+
+ return sprintf(buf, "%8u %8u\n", p->in_flight[0], p->in_flight[1]);
+}
+
#ifdef CONFIG_FAIL_MAKE_REQUEST
ssize_t part_fail_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -281,6 +289,7 @@ static DEVICE_ATTR(start, S_IRUGO, part_start_show, NULL);
static DEVICE_ATTR(size, S_IRUGO, part_size_show, NULL);
static DEVICE_ATTR(alignment_offset, S_IRUGO, part_alignment_offset_show, NULL);
static DEVICE_ATTR(stat, S_IRUGO, part_stat_show, NULL);
+static DEVICE_ATTR(inflight, S_IRUGO, part_inflight_show, NULL);
#ifdef CONFIG_FAIL_MAKE_REQUEST
static struct device_attribute dev_attr_fail =
__ATTR(make-it-fail, S_IRUGO|S_IWUSR, part_fail_show, part_fail_store);
@@ -292,6 +301,7 @@ static struct attribute *part_attrs[] = {
&dev_attr_size.attr,
&dev_attr_alignment_offset.attr,
&dev_attr_stat.attr,
+ &dev_attr_inflight.attr,
#ifdef CONFIG_FAIL_MAKE_REQUEST
&dev_attr_fail.attr,
#endif
diff --git a/fs/splice.c b/fs/splice.c
index 73766d24f97..7394e9e1753 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -502,8 +502,10 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos,
len = left;
ret = __generic_file_splice_read(in, ppos, pipe, len, flags);
- if (ret > 0)
+ if (ret > 0) {
*ppos += ret;
+ file_accessed(in);
+ }
return ret;
}
@@ -963,8 +965,10 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
ret = file_remove_suid(out);
- if (!ret)
+ if (!ret) {
+ file_update_time(out);
ret = splice_from_pipe_feed(pipe, &sd, pipe_to_file);
+ }
mutex_unlock(&inode->i_mutex);
} while (ret > 0);
splice_from_pipe_end(pipe, &sd);
@@ -976,25 +980,15 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
if (ret > 0) {
unsigned long nr_pages;
+ int err;
- *ppos += ret;
nr_pages = (ret + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
- /*
- * If file or inode is SYNC and we actually wrote some data,
- * sync it.
- */
- if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(inode))) {
- int err;
-
- mutex_lock(&inode->i_mutex);
- err = generic_osync_inode(inode, mapping,
- OSYNC_METADATA|OSYNC_DATA);
- mutex_unlock(&inode->i_mutex);
-
- if (err)
- ret = err;
- }
+ err = generic_write_sync(out, *ppos, ret);
+ if (err)
+ ret = err;
+ else
+ *ppos += ret;
balance_dirty_pages_ratelimited_nr(mapping, nr_pages);
}
diff --git a/fs/sync.c b/fs/sync.c
index 103cc7fdd3d..192340930bb 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -178,19 +178,23 @@ int file_fsync(struct file *filp, struct dentry *dentry, int datasync)
}
/**
- * vfs_fsync - perform a fsync or fdatasync on a file
+ * vfs_fsync_range - helper to sync a range of data & metadata to disk
* @file: file to sync
* @dentry: dentry of @file
- * @data: only perform a fdatasync operation
+ * @start: offset in bytes of the beginning of data range to sync
+ * @end: offset in bytes of the end of data range (inclusive)
+ * @datasync: perform only datasync
*
- * Write back data and metadata for @file to disk. If @datasync is
- * set only metadata needed to access modified file data is written.
+ * Write back data in range @start..@end and metadata for @file to disk. If
+ * @datasync is set only metadata needed to access modified file data is
+ * written.
*
* In case this function is called from nfsd @file may be %NULL and
* only @dentry is set. This can only happen when the filesystem
* implements the export_operations API.
*/
-int vfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+int vfs_fsync_range(struct file *file, struct dentry *dentry, loff_t start,
+ loff_t end, int datasync)
{
const struct file_operations *fop;
struct address_space *mapping;
@@ -214,7 +218,7 @@ int vfs_fsync(struct file *file, struct dentry *dentry, int datasync)
goto out;
}
- ret = filemap_fdatawrite(mapping);
+ ret = filemap_write_and_wait_range(mapping, start, end);
/*
* We need to protect against concurrent writers, which could cause
@@ -225,12 +229,29 @@ int vfs_fsync(struct file *file, struct dentry *dentry, int datasync)
if (!ret)
ret = err;
mutex_unlock(&mapping->host->i_mutex);
- err = filemap_fdatawait(mapping);
- if (!ret)
- ret = err;
+
out:
return ret;
}
+EXPORT_SYMBOL(vfs_fsync_range);
+
+/**
+ * vfs_fsync - perform a fsync or fdatasync on a file
+ * @file: file to sync
+ * @dentry: dentry of @file
+ * @datasync: only perform a fdatasync operation
+ *
+ * Write back data and metadata for @file to disk. If @datasync is
+ * set only metadata needed to access modified file data is written.
+ *
+ * In case this function is called from nfsd @file may be %NULL and
+ * only @dentry is set. This can only happen when the filesystem
+ * implements the export_operations API.
+ */
+int vfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+ return vfs_fsync_range(file, dentry, 0, LLONG_MAX, datasync);
+}
EXPORT_SYMBOL(vfs_fsync);
static int do_fsync(unsigned int fd, int datasync)
@@ -256,6 +277,23 @@ SYSCALL_DEFINE1(fdatasync, unsigned int, fd)
return do_fsync(fd, 1);
}
+/**
+ * generic_write_sync - perform syncing after a write if file / inode is sync
+ * @file: file to which the write happened
+ * @pos: offset where the write started
+ * @count: length of the write
+ *
+ * This is just a simple wrapper about our general syncing function.
+ */
+int generic_write_sync(struct file *file, loff_t pos, loff_t count)
+{
+ if (!(file->f_flags & O_SYNC) && !IS_SYNC(file->f_mapping->host))
+ return 0;
+ return vfs_fsync_range(file, file->f_path.dentry, pos,
+ pos + count - 1, 1);
+}
+EXPORT_SYMBOL(generic_write_sync);
+
/*
* sys_sync_file_range() permits finely controlled syncing over a segment of
* a file in the range offset .. (offset+nbytes-1) inclusive. If nbytes is
diff --git a/fs/udf/directory.c b/fs/udf/directory.c
index 1d2c570704c..2ffdb6733af 100644
--- a/fs/udf/directory.c
+++ b/fs/udf/directory.c
@@ -18,59 +18,6 @@
#include <linux/string.h>
#include <linux/buffer_head.h>
-#if 0
-static uint8_t *udf_filead_read(struct inode *dir, uint8_t *tmpad,
- uint8_t ad_size, struct kernel_lb_addr fe_loc,
- int *pos, int *offset, struct buffer_head **bh,
- int *error)
-{
- int loffset = *offset;
- int block;
- uint8_t *ad;
- int remainder;
-
- *error = 0;
-
- ad = (uint8_t *)(*bh)->b_data + *offset;
- *offset += ad_size;
-
- if (!ad) {
- brelse(*bh);
- *error = 1;
- return NULL;
- }
-
- if (*offset == dir->i_sb->s_blocksize) {
- brelse(*bh);
- block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos);
- if (!block)
- return NULL;
- *bh = udf_tread(dir->i_sb, block);
- if (!*bh)
- return NULL;
- } else if (*offset > dir->i_sb->s_blocksize) {
- ad = tmpad;
-
- remainder = dir->i_sb->s_blocksize - loffset;
- memcpy((uint8_t *)ad, (*bh)->b_data + loffset, remainder);
-
- brelse(*bh);
- block = udf_get_lb_pblock(dir->i_sb, fe_loc, ++*pos);
- if (!block)
- return NULL;
- (*bh) = udf_tread(dir->i_sb, block);
- if (!*bh)
- return NULL;
-
- memcpy((uint8_t *)ad + remainder, (*bh)->b_data,
- ad_size - remainder);
- *offset = ad_size - remainder;
- }
-
- return ad;
-}
-#endif
-
struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t *nf_pos,
struct udf_fileident_bh *fibh,
struct fileIdentDesc *cfi,
@@ -248,39 +195,6 @@ struct fileIdentDesc *udf_get_fileident(void *buffer, int bufsize, int *offset)
return fi;
}
-#if 0
-static struct extent_ad *udf_get_fileextent(void *buffer, int bufsize, int *offset)
-{
- struct extent_ad *ext;
- struct fileEntry *fe;
- uint8_t *ptr;
-
- if ((!buffer) || (!offset)) {
- printk(KERN_ERR "udf: udf_get_fileextent() invalidparms\n");
- return NULL;
- }
-
- fe = (struct fileEntry *)buffer;
-
- if (fe->descTag.tagIdent != cpu_to_le16(TAG_IDENT_FE)) {
- udf_debug("0x%x != TAG_IDENT_FE\n",
- le16_to_cpu(fe->descTag.tagIdent));
- return NULL;
- }
-
- ptr = (uint8_t *)(fe->extendedAttr) +
- le32_to_cpu(fe->lengthExtendedAttr);
-
- if ((*offset > 0) && (*offset < le32_to_cpu(fe->lengthAllocDescs)))
- ptr += *offset;
-
- ext = (struct extent_ad *)ptr;
-
- *offset = *offset + sizeof(struct extent_ad);
- return ext;
-}
-#endif
-
struct short_ad *udf_get_fileshortad(uint8_t *ptr, int maxoffset, uint32_t *offset,
int inc)
{
diff --git a/fs/udf/file.c b/fs/udf/file.c
index 7464305382b..b80cbd78833 100644
--- a/fs/udf/file.c
+++ b/fs/udf/file.c
@@ -193,9 +193,11 @@ int udf_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
static int udf_release_file(struct inode *inode, struct file *filp)
{
if (filp->f_mode & FMODE_WRITE) {
+ mutex_lock(&inode->i_mutex);
lock_kernel();
udf_discard_prealloc(inode);
unlock_kernel();
+ mutex_unlock(&inode->i_mutex);
}
return 0;
}
diff --git a/fs/udf/inode.c b/fs/udf/inode.c
index e7533f78563..6d24c2c63f9 100644
--- a/fs/udf/inode.c
+++ b/fs/udf/inode.c
@@ -90,19 +90,16 @@ no_delete:
}
/*
- * If we are going to release inode from memory, we discard preallocation and
- * truncate last inode extent to proper length. We could use drop_inode() but
- * it's called under inode_lock and thus we cannot mark inode dirty there. We
- * use clear_inode() but we have to make sure to write inode as it's not written
- * automatically.
+ * If we are going to release inode from memory, we truncate last inode extent
+ * to proper length. We could use drop_inode() but it's called under inode_lock
+ * and thus we cannot mark inode dirty there. We use clear_inode() but we have
+ * to make sure to write inode as it's not written automatically.
*/
void udf_clear_inode(struct inode *inode)
{
struct udf_inode_info *iinfo;
if (!(inode->i_sb->s_flags & MS_RDONLY)) {
lock_kernel();
- /* Discard preallocation for directories, symlinks, etc. */
- udf_discard_prealloc(inode);
udf_truncate_tail_extent(inode);
unlock_kernel();
write_inode_now(inode, 0);
@@ -664,8 +661,12 @@ static struct buffer_head *inode_getblk(struct inode *inode, sector_t block,
udf_split_extents(inode, &c, offset, newblocknum, laarr, &endnum);
#ifdef UDF_PREALLOCATE
- /* preallocate blocks */
- udf_prealloc_extents(inode, c, lastblock, laarr, &endnum);
+ /* We preallocate blocks only for regular files. It also makes sense
+ * for directories but there's a problem when to drop the
+ * preallocation. We might use some delayed work for that but I feel
+ * it's overengineering for a filesystem like UDF. */
+ if (S_ISREG(inode->i_mode))
+ udf_prealloc_extents(inode, c, lastblock, laarr, &endnum);
#endif
/* merge any continuous blocks in laarr */
diff --git a/fs/udf/lowlevel.c b/fs/udf/lowlevel.c
index 1b88fd5df05..43e24a3b8e1 100644
--- a/fs/udf/lowlevel.c
+++ b/fs/udf/lowlevel.c
@@ -36,14 +36,10 @@ unsigned int udf_get_last_session(struct super_block *sb)
ms_info.addr_format = CDROM_LBA;
i = ioctl_by_bdev(bdev, CDROMMULTISESSION, (unsigned long)&ms_info);
-#define WE_OBEY_THE_WRITTEN_STANDARDS 1
-
if (i == 0) {
udf_debug("XA disk: %s, vol_desc_start=%d\n",
(ms_info.xa_flag ? "yes" : "no"), ms_info.addr.lba);
-#if WE_OBEY_THE_WRITTEN_STANDARDS
if (ms_info.xa_flag) /* necessary for a valid ms_info.addr */
-#endif
vol_desc_start = ms_info.addr.lba;
} else {
udf_debug("CDROMMULTISESSION not supported: rc=%d\n", i);
diff --git a/fs/udf/namei.c b/fs/udf/namei.c
index 6a29fa34c47..21dad8c608f 100644
--- a/fs/udf/namei.c
+++ b/fs/udf/namei.c
@@ -943,7 +943,6 @@ static int udf_symlink(struct inode *dir, struct dentry *dentry,
pc->componentType = 1;
pc->lengthComponentIdent = 0;
pc->componentFileVersionNum = 0;
- pc += sizeof(struct pathComponent);
elen += sizeof(struct pathComponent);
}
diff --git a/fs/xfs/linux-2.6/xfs_lrw.c b/fs/xfs/linux-2.6/xfs_lrw.c
index 7078974a6ee..fde63a3c4ec 100644
--- a/fs/xfs/linux-2.6/xfs_lrw.c
+++ b/fs/xfs/linux-2.6/xfs_lrw.c
@@ -817,7 +817,8 @@ write_retry:
xfs_iunlock(xip, iolock);
if (need_i_mutex)
mutex_unlock(&inode->i_mutex);
- error2 = sync_page_range(inode, mapping, pos, ret);
+ error2 = filemap_write_and_wait_range(mapping, pos,
+ pos + ret - 1);
if (!error)
error = error2;
if (need_i_mutex)
diff --git a/include/asm-generic/Kbuild.asm b/include/asm-generic/Kbuild.asm
index 290910e4ede..96d7c9804dc 100644
--- a/include/asm-generic/Kbuild.asm
+++ b/include/asm-generic/Kbuild.asm
@@ -3,6 +3,11 @@ ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/kvm.h \
header-y += kvm.h
endif
+ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/kvm_para.h \
+ $(srctree)/include/asm-$(SRCARCH)/kvm_para.h),)
+header-y += kvm_para.h
+endif
+
ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/a.out.h \
$(srctree)/include/asm-$(SRCARCH)/a.out.h),)
unifdef-y += a.out.h
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 334a3593cdf..cff4a101f26 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -268,6 +268,10 @@ ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/kvm.h \
$(srctree)/include/asm-$(SRCARCH)/kvm.h),)
unifdef-y += kvm.h
endif
+ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/include/asm/kvm_para.h \
+ $(srctree)/include/asm-$(SRCARCH)/kvm_para.h),)
+unifdef-y += kvm_para.h
+endif
unifdef-y += llc.h
unifdef-y += loop.h
unifdef-y += lp.h
diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h
index 9b93cafa82a..ab94335b4bb 100644
--- a/include/linux/amba/bus.h
+++ b/include/linux/amba/bus.h
@@ -36,6 +36,11 @@ struct amba_driver {
struct amba_id *id_table;
};
+enum amba_vendor {
+ AMBA_VENDOR_ARM = 0x41,
+ AMBA_VENDOR_ST = 0x80,
+};
+
#define amba_get_drvdata(d) dev_get_drvdata(&d->dev)
#define amba_set_drvdata(d,p) dev_set_drvdata(&d->dev, p)
diff --git a/include/linux/amba/pl093.h b/include/linux/amba/pl093.h
new file mode 100644
index 00000000000..2983e3671ad
--- /dev/null
+++ b/include/linux/amba/pl093.h
@@ -0,0 +1,80 @@
+/* linux/amba/pl093.h
+ *
+ * Copyright (c) 2008 Simtec Electronics
+ * http://armlinux.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * AMBA PL093 SSMC (synchronous static memory controller)
+ * See DDI0236.pdf (r0p4) for more details
+ *
+ * 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.
+*/
+
+#define SMB_BANK(x) ((x) * 0x20) /* each bank control set is 0x20 apart */
+
+/* Offsets for SMBxxxxRy registers */
+
+#define SMBIDCYR (0x00)
+#define SMBWSTRDR (0x04)
+#define SMBWSTWRR (0x08)
+#define SMBWSTOENR (0x0C)
+#define SMBWSTWENR (0x10)
+#define SMBCR (0x14)
+#define SMBSR (0x18)
+#define SMBWSTBRDR (0x1C)
+
+/* Masks for SMB registers */
+#define IDCY_MASK (0xf)
+#define WSTRD_MASK (0xf)
+#define WSTWR_MASK (0xf)
+#define WSTOEN_MASK (0xf)
+#define WSTWEN_MASK (0xf)
+
+/* Notes from datasheet:
+ * WSTOEN <= WSTRD
+ * WSTWEN <= WSTWR
+ *
+ * WSTOEN is not used with nWAIT
+ */
+
+/* SMBCR bit definitions */
+#define SMBCR_BIWRITEEN (1 << 21)
+#define SMBCR_ADDRVALIDWRITEEN (1 << 20)
+#define SMBCR_SYNCWRITE (1 << 17)
+#define SMBCR_BMWRITE (1 << 16)
+#define SMBCR_WRAPREAD (1 << 14)
+#define SMBCR_BIREADEN (1 << 13)
+#define SMBCR_ADDRVALIDREADEN (1 << 12)
+#define SMBCR_SYNCREAD (1 << 9)
+#define SMBCR_BMREAD (1 << 8)
+#define SMBCR_SMBLSPOL (1 << 6)
+#define SMBCR_WP (1 << 3)
+#define SMBCR_WAITEN (1 << 2)
+#define SMBCR_WAITPOL (1 << 1)
+#define SMBCR_RBLE (1 << 0)
+
+#define SMBCR_BURSTLENWRITE_MASK (3 << 18)
+#define SMBCR_BURSTLENWRITE_4 (0 << 18)
+#define SMBCR_BURSTLENWRITE_8 (1 << 18)
+#define SMBCR_BURSTLENWRITE_RESERVED (2 << 18)
+#define SMBCR_BURSTLENWRITE_CONTINUOUS (3 << 18)
+
+#define SMBCR_BURSTLENREAD_MASK (3 << 10)
+#define SMBCR_BURSTLENREAD_4 (0 << 10)
+#define SMBCR_BURSTLENREAD_8 (1 << 10)
+#define SMBCR_BURSTLENREAD_16 (2 << 10)
+#define SMBCR_BURSTLENREAD_CONTINUOUS (3 << 10)
+
+#define SMBCR_MW_MASK (3 << 4)
+#define SMBCR_MW_8BIT (0 << 4)
+#define SMBCR_MW_16BIT (1 << 4)
+#define SMBCR_MW_M32BIT (2 << 4)
+
+/* SSMC status registers */
+#define SSMCCSR (0x200)
+#define SSMCCR (0x204)
+#define SSMCITCR (0x208)
+#define SSMCITIP (0x20C)
+#define SSMCITIOP (0x210)
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 2892b710771..5be93f18d84 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -142,56 +142,51 @@ struct bio {
*
* bit 0 -- data direction
* If not set, bio is a read from device. If set, it's a write to device.
- * bit 1 -- rw-ahead when set
- * bit 2 -- barrier
+ * bit 1 -- fail fast device errors
+ * bit 2 -- fail fast transport errors
+ * bit 3 -- fail fast driver errors
+ * bit 4 -- rw-ahead when set
+ * bit 5 -- barrier
* Insert a serialization point in the IO queue, forcing previously
* submitted IO to be completed before this one is issued.
- * bit 3 -- synchronous I/O hint.
- * bit 4 -- Unplug the device immediately after submitting this bio.
- * bit 5 -- metadata request
+ * bit 6 -- synchronous I/O hint.
+ * bit 7 -- Unplug the device immediately after submitting this bio.
+ * bit 8 -- metadata request
* Used for tracing to differentiate metadata and data IO. May also
* get some preferential treatment in the IO scheduler
- * bit 6 -- discard sectors
+ * bit 9 -- discard sectors
* Informs the lower level device that this range of sectors is no longer
* used by the file system and may thus be freed by the device. Used
* for flash based storage.
- * bit 7 -- fail fast device errors
- * bit 8 -- fail fast transport errors
- * bit 9 -- fail fast driver errors
* Don't want driver retries for any fast fail whatever the reason.
* bit 10 -- Tell the IO scheduler not to wait for more requests after this
one has been submitted, even if it is a SYNC request.
*/
-#define BIO_RW 0 /* Must match RW in req flags (blkdev.h) */
-#define BIO_RW_AHEAD 1 /* Must match FAILFAST in req flags */
-#define BIO_RW_BARRIER 2
-#define BIO_RW_SYNCIO 3
-#define BIO_RW_UNPLUG 4
-#define BIO_RW_META 5
-#define BIO_RW_DISCARD 6
-#define BIO_RW_FAILFAST_DEV 7
-#define BIO_RW_FAILFAST_TRANSPORT 8
-#define BIO_RW_FAILFAST_DRIVER 9
-#define BIO_RW_NOIDLE 10
-
-#define bio_rw_flagged(bio, flag) ((bio)->bi_rw & (1 << (flag)))
+enum bio_rw_flags {
+ BIO_RW,
+ BIO_RW_FAILFAST_DEV,
+ BIO_RW_FAILFAST_TRANSPORT,
+ BIO_RW_FAILFAST_DRIVER,
+ /* above flags must match REQ_* */
+ BIO_RW_AHEAD,
+ BIO_RW_BARRIER,
+ BIO_RW_SYNCIO,
+ BIO_RW_UNPLUG,
+ BIO_RW_META,
+ BIO_RW_DISCARD,
+ BIO_RW_NOIDLE,
+};
/*
- * Old defines, these should eventually be replaced by direct usage of
- * bio_rw_flagged()
+ * First four bits must match between bio->bi_rw and rq->cmd_flags, make
+ * that explicit here.
*/
-#define bio_barrier(bio) bio_rw_flagged(bio, BIO_RW_BARRIER)
-#define bio_sync(bio) bio_rw_flagged(bio, BIO_RW_SYNCIO)
-#define bio_unplug(bio) bio_rw_flagged(bio, BIO_RW_UNPLUG)
-#define bio_failfast_dev(bio) bio_rw_flagged(bio, BIO_RW_FAILFAST_DEV)
-#define bio_failfast_transport(bio) \
- bio_rw_flagged(bio, BIO_RW_FAILFAST_TRANSPORT)
-#define bio_failfast_driver(bio) \
- bio_rw_flagged(bio, BIO_RW_FAILFAST_DRIVER)
-#define bio_rw_ahead(bio) bio_rw_flagged(bio, BIO_RW_AHEAD)
-#define bio_rw_meta(bio) bio_rw_flagged(bio, BIO_RW_META)
-#define bio_discard(bio) bio_rw_flagged(bio, BIO_RW_DISCARD)
-#define bio_noidle(bio) bio_rw_flagged(bio, BIO_RW_NOIDLE)
+#define BIO_RW_RQ_MASK 0xf
+
+static inline bool bio_rw_flagged(struct bio *bio, enum bio_rw_flags flag)
+{
+ return (bio->bi_rw & (1 << flag)) != 0;
+}
/*
* upper 16 bits of bi_rw define the io priority of this bio
@@ -216,7 +211,7 @@ struct bio {
#define bio_offset(bio) bio_iovec((bio))->bv_offset
#define bio_segments(bio) ((bio)->bi_vcnt - (bio)->bi_idx)
#define bio_sectors(bio) ((bio)->bi_size >> 9)
-#define bio_empty_barrier(bio) (bio_barrier(bio) && !bio_has_data(bio) && !bio_discard(bio))
+#define bio_empty_barrier(bio) (bio_rw_flagged(bio, BIO_RW_BARRIER) && !bio_has_data(bio) && !bio_rw_flagged(bio, BIO_RW_DISCARD))
static inline unsigned int bio_cur_bytes(struct bio *bio)
{
diff --git a/include/linux/blk-iopoll.h b/include/linux/blk-iopoll.h
new file mode 100644
index 00000000000..308734d3d4a
--- /dev/null
+++ b/include/linux/blk-iopoll.h
@@ -0,0 +1,48 @@
+#ifndef BLK_IOPOLL_H
+#define BLK_IOPOLL_H
+
+struct blk_iopoll;
+typedef int (blk_iopoll_fn)(struct blk_iopoll *, int);
+
+struct blk_iopoll {
+ struct list_head list;
+ unsigned long state;
+ unsigned long data;
+ int weight;
+ int max;
+ blk_iopoll_fn *poll;
+};
+
+enum {
+ IOPOLL_F_SCHED = 0,
+ IOPOLL_F_DISABLE = 1,
+};
+
+/*
+ * Returns 0 if we successfully set the IOPOLL_F_SCHED bit, indicating
+ * that we were the first to acquire this iop for scheduling. If this iop
+ * is currently disabled, return "failure".
+ */
+static inline int blk_iopoll_sched_prep(struct blk_iopoll *iop)
+{
+ if (!test_bit(IOPOLL_F_DISABLE, &iop->state))
+ return test_and_set_bit(IOPOLL_F_SCHED, &iop->state);
+
+ return 1;
+}
+
+static inline int blk_iopoll_disable_pending(struct blk_iopoll *iop)
+{
+ return test_bit(IOPOLL_F_DISABLE, &iop->state);
+}
+
+extern void blk_iopoll_sched(struct blk_iopoll *);
+extern void blk_iopoll_init(struct blk_iopoll *, int, blk_iopoll_fn *);
+extern void blk_iopoll_complete(struct blk_iopoll *);
+extern void __blk_iopoll_complete(struct blk_iopoll *);
+extern void blk_iopoll_enable(struct blk_iopoll *);
+extern void blk_iopoll_disable(struct blk_iopoll *);
+
+extern int blk_iopoll_enabled;
+
+#endif
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 69103e053c9..e23a86cae5a 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -86,13 +86,14 @@ enum {
};
/*
- * request type modified bits. first two bits match BIO_RW* bits, important
+ * request type modified bits. first four bits match BIO_RW* bits, important
*/
enum rq_flag_bits {
__REQ_RW, /* not set, read. set, write */
__REQ_FAILFAST_DEV, /* no driver retries of device errors */
__REQ_FAILFAST_TRANSPORT, /* no driver retries of transport errors */
__REQ_FAILFAST_DRIVER, /* no driver retries of driver errors */
+ /* above flags must match BIO_RW_* */
__REQ_DISCARD, /* request to discard sectors */
__REQ_SORTED, /* elevator knows about this request */
__REQ_SOFTBARRIER, /* may not be passed by ioscheduler */
@@ -114,6 +115,7 @@ enum rq_flag_bits {
__REQ_INTEGRITY, /* integrity metadata has been remapped */
__REQ_NOIDLE, /* Don't anticipate more IO after this one */
__REQ_IO_STAT, /* account I/O stat */
+ __REQ_MIXED_MERGE, /* merge of different types, fail separately */
__REQ_NR_BITS, /* stops here */
};
@@ -142,6 +144,10 @@ enum rq_flag_bits {
#define REQ_INTEGRITY (1 << __REQ_INTEGRITY)
#define REQ_NOIDLE (1 << __REQ_NOIDLE)
#define REQ_IO_STAT (1 << __REQ_IO_STAT)
+#define REQ_MIXED_MERGE (1 << __REQ_MIXED_MERGE)
+
+#define REQ_FAILFAST_MASK (REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | \
+ REQ_FAILFAST_DRIVER)
#define BLK_MAX_CDB 16
@@ -453,10 +459,12 @@ struct request_queue
#define QUEUE_FLAG_NONROT 14 /* non-rotational device (SSD) */
#define QUEUE_FLAG_VIRT QUEUE_FLAG_NONROT /* paravirt device */
#define QUEUE_FLAG_IO_STAT 15 /* do IO stats */
+#define QUEUE_FLAG_CQ 16 /* hardware does queuing */
#define QUEUE_FLAG_DEFAULT ((1 << QUEUE_FLAG_IO_STAT) | \
(1 << QUEUE_FLAG_CLUSTER) | \
- (1 << QUEUE_FLAG_STACKABLE))
+ (1 << QUEUE_FLAG_STACKABLE) | \
+ (1 << QUEUE_FLAG_SAME_COMP))
static inline int queue_is_locked(struct request_queue *q)
{
@@ -575,6 +583,7 @@ enum {
#define blk_queue_plugged(q) test_bit(QUEUE_FLAG_PLUGGED, &(q)->queue_flags)
#define blk_queue_tagged(q) test_bit(QUEUE_FLAG_QUEUED, &(q)->queue_flags)
+#define blk_queue_queuing(q) test_bit(QUEUE_FLAG_CQ, &(q)->queue_flags)
#define blk_queue_stopped(q) test_bit(QUEUE_FLAG_STOPPED, &(q)->queue_flags)
#define blk_queue_nomerges(q) test_bit(QUEUE_FLAG_NOMERGES, &(q)->queue_flags)
#define blk_queue_nonrot(q) test_bit(QUEUE_FLAG_NONROT, &(q)->queue_flags)
@@ -828,11 +837,13 @@ static inline void blk_run_address_space(struct address_space *mapping)
}
/*
- * blk_rq_pos() : the current sector
- * blk_rq_bytes() : bytes left in the entire request
- * blk_rq_cur_bytes() : bytes left in the current segment
- * blk_rq_sectors() : sectors left in the entire request
- * blk_rq_cur_sectors() : sectors left in the current segment
+ * blk_rq_pos() : the current sector
+ * blk_rq_bytes() : bytes left in the entire request
+ * blk_rq_cur_bytes() : bytes left in the current segment
+ * blk_rq_err_bytes() : bytes left till the next error boundary
+ * blk_rq_sectors() : sectors left in the entire request
+ * blk_rq_cur_sectors() : sectors left in the current segment
+ * blk_rq_err_sectors() : sectors left till the next error boundary
*/
static inline sector_t blk_rq_pos(const struct request *rq)
{
@@ -849,6 +860,8 @@ static inline int blk_rq_cur_bytes(const struct request *rq)
return rq->bio ? bio_cur_bytes(rq->bio) : 0;
}
+extern unsigned int blk_rq_err_bytes(const struct request *rq);
+
static inline unsigned int blk_rq_sectors(const struct request *rq)
{
return blk_rq_bytes(rq) >> 9;
@@ -859,6 +872,11 @@ static inline unsigned int blk_rq_cur_sectors(const struct request *rq)
return blk_rq_cur_bytes(rq) >> 9;
}
+static inline unsigned int blk_rq_err_sectors(const struct request *rq)
+{
+ return blk_rq_err_bytes(rq) >> 9;
+}
+
/*
* Request issue related functions.
*/
@@ -885,10 +903,12 @@ extern bool blk_end_request(struct request *rq, int error,
unsigned int nr_bytes);
extern void blk_end_request_all(struct request *rq, int error);
extern bool blk_end_request_cur(struct request *rq, int error);
+extern bool blk_end_request_err(struct request *rq, int error);
extern bool __blk_end_request(struct request *rq, int error,
unsigned int nr_bytes);
extern void __blk_end_request_all(struct request *rq, int error);
extern bool __blk_end_request_cur(struct request *rq, int error);
+extern bool __blk_end_request_err(struct request *rq, int error);
extern void blk_complete_request(struct request *);
extern void __blk_complete_request(struct request *);
@@ -915,6 +935,7 @@ extern void blk_queue_alignment_offset(struct request_queue *q,
unsigned int alignment);
extern void blk_limits_io_min(struct queue_limits *limits, unsigned int min);
extern void blk_queue_io_min(struct request_queue *q, unsigned int min);
+extern void blk_limits_io_opt(struct queue_limits *limits, unsigned int opt);
extern void blk_queue_io_opt(struct request_queue *q, unsigned int opt);
extern void blk_set_default_limits(struct queue_limits *lim);
extern int blk_stack_limits(struct queue_limits *t, struct queue_limits *b,
@@ -977,15 +998,18 @@ static inline struct request *blk_map_queue_find_tag(struct blk_queue_tag *bqt,
}
extern int blkdev_issue_flush(struct block_device *, sector_t *);
-extern int blkdev_issue_discard(struct block_device *,
- sector_t sector, sector_t nr_sects, gfp_t);
+#define DISCARD_FL_WAIT 0x01 /* wait for completion */
+#define DISCARD_FL_BARRIER 0x02 /* issue DISCARD_BARRIER request */
+extern int blkdev_issue_discard(struct block_device *, sector_t sector,
+ sector_t nr_sects, gfp_t, int flags);
static inline int sb_issue_discard(struct super_block *sb,
sector_t block, sector_t nr_blocks)
{
block <<= (sb->s_blocksize_bits - 9);
nr_blocks <<= (sb->s_blocksize_bits - 9);
- return blkdev_issue_discard(sb->s_bdev, block, nr_blocks, GFP_KERNEL);
+ return blkdev_issue_discard(sb->s_bdev, block, nr_blocks, GFP_KERNEL,
+ DISCARD_FL_BARRIER);
}
extern int blk_verify_command(unsigned char *cmd, fmode_t has_write_perm);
diff --git a/include/linux/dtlk.h b/include/linux/dtlk.h
index 2896d90118a..22a7b9a5f5d 100644
--- a/include/linux/dtlk.h
+++ b/include/linux/dtlk.h
@@ -1,22 +1,3 @@
-#if 0
-
-#define TRACE_TXT(text) \
- { \
- if(dtlk_trace) \
- { \
- console_print(text); \
- console_print("\n"); \
- } \
- }
-
-#define TRACE_CHR(chr) \
- { \
- if(dtlk_trace) \
- console_print(chr); \
- } \
-
-#endif
-
#define DTLK_MINOR 0
#define DTLK_IO_EXTENT 0x02
diff --git a/include/linux/enclosure.h b/include/linux/enclosure.h
index 4332442b1b5..90d1c218411 100644
--- a/include/linux/enclosure.h
+++ b/include/linux/enclosure.h
@@ -122,8 +122,9 @@ enclosure_component_register(struct enclosure_device *, unsigned int,
enum enclosure_component_type, const char *);
int enclosure_add_device(struct enclosure_device *enclosure, int component,
struct device *dev);
-int enclosure_remove_device(struct enclosure_device *enclosure, int component);
-struct enclosure_device *enclosure_find(struct device *dev);
+int enclosure_remove_device(struct enclosure_device *, struct device *);
+struct enclosure_device *enclosure_find(struct device *dev,
+ struct enclosure_device *start);
int enclosure_for_each_device(int (*fn)(struct enclosure_device *, void *),
void *data);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a79f48373e7..b21cf6b9c80 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -161,8 +161,8 @@ struct inodes_stat_t {
* These aren't really reads or writes, they pass down information about
* parts of device that are now unused by the file system.
*/
-#define DISCARD_NOBARRIER (1 << BIO_RW_DISCARD)
-#define DISCARD_BARRIER ((1 << BIO_RW_DISCARD) | (1 << BIO_RW_BARRIER))
+#define DISCARD_NOBARRIER (WRITE | (1 << BIO_RW_DISCARD))
+#define DISCARD_BARRIER (DISCARD_NOBARRIER | (1 << BIO_RW_BARRIER))
#define SEL_IN 1
#define SEL_OUT 2
@@ -1455,11 +1455,6 @@ int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags);
#define DT_SOCK 12
#define DT_WHT 14
-#define OSYNC_METADATA (1<<0)
-#define OSYNC_DATA (1<<1)
-#define OSYNC_INODE (1<<2)
-int generic_osync_inode(struct inode *, struct address_space *, int);
-
/*
* This is the "filldir" function type, used by readdir() to let
* the kernel specify what kind of dirent layout it wants to have.
@@ -2086,6 +2081,8 @@ extern int write_inode_now(struct inode *, int);
extern int filemap_fdatawrite(struct address_space *);
extern int filemap_flush(struct address_space *);
extern int filemap_fdatawait(struct address_space *);
+extern int filemap_fdatawait_range(struct address_space *, loff_t lstart,
+ loff_t lend);
extern int filemap_write_and_wait(struct address_space *mapping);
extern int filemap_write_and_wait_range(struct address_space *mapping,
loff_t lstart, loff_t lend);
@@ -2096,7 +2093,10 @@ extern int __filemap_fdatawrite_range(struct address_space *mapping,
extern int filemap_fdatawrite_range(struct address_space *mapping,
loff_t start, loff_t end);
+extern int vfs_fsync_range(struct file *file, struct dentry *dentry,
+ loff_t start, loff_t end, int datasync);
extern int vfs_fsync(struct file *file, struct dentry *dentry, int datasync);
+extern int generic_write_sync(struct file *file, loff_t pos, loff_t count);
extern void sync_supers(void);
extern void emergency_sync(void);
extern void emergency_remount(void);
@@ -2202,9 +2202,9 @@ extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *);
extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk);
extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsigned long, loff_t);
+extern ssize_t __generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long,
+ loff_t *);
extern ssize_t generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, loff_t);
-extern ssize_t generic_file_aio_write_nolock(struct kiocb *, const struct iovec *,
- unsigned long, loff_t);
extern ssize_t generic_file_direct_write(struct kiocb *, const struct iovec *,
unsigned long *, loff_t, loff_t *, size_t, size_t);
extern ssize_t generic_file_buffered_write(struct kiocb *, const struct iovec *,
@@ -2214,6 +2214,10 @@ extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t l
extern int generic_segment_checks(const struct iovec *iov,
unsigned long *nr_segs, size_t *count, int access_flags);
+/* fs/block_dev.c */
+extern ssize_t blkdev_aio_write(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos);
+
/* fs/splice.c */
extern ssize_t generic_file_splice_read(struct file *, loff_t *,
struct pipe_inode_info *, size_t, unsigned int);
diff --git a/include/linux/genhd.h b/include/linux/genhd.h
index 45fc320a53c..44263cb2712 100644
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -98,7 +98,7 @@ struct hd_struct {
int make_it_fail;
#endif
unsigned long stamp;
- int in_flight;
+ int in_flight[2];
#ifdef CONFIG_SMP
struct disk_stats *dkstats;
#else
@@ -322,18 +322,23 @@ static inline void free_part_stats(struct hd_struct *part)
#define part_stat_sub(cpu, gendiskp, field, subnd) \
part_stat_add(cpu, gendiskp, field, -subnd)
-static inline void part_inc_in_flight(struct hd_struct *part)
+static inline void part_inc_in_flight(struct hd_struct *part, int rw)
{
- part->in_flight++;
+ part->in_flight[rw]++;
if (part->partno)
- part_to_disk(part)->part0.in_flight++;
+ part_to_disk(part)->part0.in_flight[rw]++;
}
-static inline void part_dec_in_flight(struct hd_struct *part)
+static inline void part_dec_in_flight(struct hd_struct *part, int rw)
{
- part->in_flight--;
+ part->in_flight[rw]--;
if (part->partno)
- part_to_disk(part)->part0.in_flight--;
+ part_to_disk(part)->part0.in_flight[rw]--;
+}
+
+static inline int part_in_flight(struct hd_struct *part)
+{
+ return part->in_flight[0] + part->in_flight[1];
}
/* block/blk-core.c */
@@ -546,6 +551,8 @@ extern ssize_t part_size_show(struct device *dev,
struct device_attribute *attr, char *buf);
extern ssize_t part_stat_show(struct device *dev,
struct device_attribute *attr, char *buf);
+extern ssize_t part_inflight_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
#ifdef CONFIG_FAIL_MAKE_REQUEST
extern ssize_t part_fail_show(struct device *dev,
struct device_attribute *attr, char *buf);
diff --git a/include/linux/gfs2_ondisk.h b/include/linux/gfs2_ondisk.h
index c56b4bce56d..b80c88dedbb 100644
--- a/include/linux/gfs2_ondisk.h
+++ b/include/linux/gfs2_ondisk.h
@@ -333,6 +333,28 @@ struct gfs2_leaf {
/*
* Extended attribute header format
+ *
+ * This works in a similar way to dirents. There is a fixed size header
+ * followed by a variable length section made up of the name and the
+ * associated data. In the case of a "stuffed" entry, the value is
+ * inline directly after the name, the ea_num_ptrs entry will be
+ * zero in that case. For non-"stuffed" entries, there will be
+ * a set of pointers (aligned to 8 byte boundary) to the block(s)
+ * containing the value.
+ *
+ * The blocks containing the values and the blocks containing the
+ * extended attribute headers themselves all start with the common
+ * metadata header. Each inode, if it has extended attributes, will
+ * have either a single block containing the extended attribute headers
+ * or a single indirect block pointing to blocks containing the
+ * extended attribure headers.
+ *
+ * The maximim size of the data part of an extended attribute is 64k
+ * so the number of blocks required depends upon block size. Since the
+ * block size also determines the number of pointers in an indirect
+ * block, its a fairly complicated calculation to work out the maximum
+ * number of blocks that an inode may have relating to extended attributes.
+ *
*/
#define GFS2_EA_MAX_NAME_LEN 255
diff --git a/include/linux/hid-debug.h b/include/linux/hid-debug.h
index 50d568ec178..53744fa1c8b 100644
--- a/include/linux/hid-debug.h
+++ b/include/linux/hid-debug.h
@@ -2,7 +2,7 @@
#define __HID_DEBUG_H
/*
- * Copyright (c) 2007 Jiri Kosina
+ * Copyright (c) 2007-2009 Jiri Kosina
*/
/*
@@ -22,24 +22,44 @@
*
*/
-#ifdef CONFIG_HID_DEBUG
+#define HID_DEBUG_BUFSIZE 512
-void hid_dump_input(struct hid_usage *, __s32);
-void hid_dump_device(struct hid_device *);
-void hid_dump_field(struct hid_field *, int);
-void hid_resolv_usage(unsigned);
-void hid_resolv_event(__u8, __u16);
+#ifdef CONFIG_DEBUG_FS
+
+void hid_dump_input(struct hid_device *, struct hid_usage *, __s32);
+void hid_dump_device(struct hid_device *, struct seq_file *);
+void hid_dump_field(struct hid_field *, int, struct seq_file *);
+char *hid_resolv_usage(unsigned, struct seq_file *);
+void hid_debug_register(struct hid_device *, const char *);
+void hid_debug_unregister(struct hid_device *);
+void hid_debug_init(void);
+void hid_debug_exit(void);
+void hid_debug_event(struct hid_device *, char *);
-#else
-#define hid_dump_input(a,b) do { } while (0)
-#define hid_dump_device(c) do { } while (0)
-#define hid_dump_field(a,b) do { } while (0)
-#define hid_resolv_usage(a) do { } while (0)
-#define hid_resolv_event(a,b) do { } while (0)
+struct hid_debug_list {
+ char *hid_debug_buf;
+ int head;
+ int tail;
+ struct fasync_struct *fasync;
+ struct hid_device *hdev;
+ struct list_head node;
+ struct mutex read_mutex;
+};
-#endif /* CONFIG_HID_DEBUG */
+#else
+#define hid_dump_input(a,b,c) do { } while (0)
+#define hid_dump_device(a,b) do { } while (0)
+#define hid_dump_field(a,b,c) do { } while (0)
+#define hid_resolv_usage(a,b) do { } while (0)
+#define hid_debug_register(a, b) do { } while (0)
+#define hid_debug_unregister(a) do { } while (0)
+#define hid_debug_init() do { } while (0)
+#define hid_debug_exit() do { } while (0)
+#define hid_debug_event(a,b) do { } while (0)
+
+#endif
#endif
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 53489fd4d70..a0ebdace7ba 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -500,6 +500,14 @@ struct hid_device { /* device report descriptor */
/* handler for raw output data, used by hidraw */
int (*hid_output_raw_report) (struct hid_device *, __u8 *, size_t);
+
+ /* debugging support via debugfs */
+ unsigned short debug;
+ struct dentry *debug_dir;
+ struct dentry *debug_rdesc;
+ struct dentry *debug_events;
+ struct list_head debug_list;
+ wait_queue_head_t debug_wait;
};
static inline void *hid_get_drvdata(struct hid_device *hdev)
@@ -657,9 +665,7 @@ struct hid_ll_driver {
/* HID core API */
-#ifdef CONFIG_HID_DEBUG
extern int hid_debug;
-#endif
extern int hid_add_device(struct hid_device *);
extern void hid_destroy_device(struct hid_device *);
@@ -815,21 +821,9 @@ int hid_pidff_init(struct hid_device *hid);
#define hid_pidff_init NULL
#endif
-#ifdef CONFIG_HID_DEBUG
#define dbg_hid(format, arg...) if (hid_debug) \
printk(KERN_DEBUG "%s: " format ,\
__FILE__ , ## arg)
-#define dbg_hid_line(format, arg...) if (hid_debug) \
- printk(format, ## arg)
-#else
-static inline int __attribute__((format(printf, 1, 2)))
-dbg_hid(const char *fmt, ...)
-{
- return 0;
-}
-#define dbg_hid_line dbg_hid
-#endif /* HID_DEBUG */
-
#define err_hid(format, arg...) printk(KERN_ERR "%s: " format "\n" , \
__FILE__ , ## arg)
#endif /* HID_FF */
diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h
index 0dc80ef2497..3fd21d7cb6b 100644
--- a/include/linux/i2c/twl4030.h
+++ b/include/linux/i2c/twl4030.h
@@ -25,6 +25,9 @@
#ifndef __TWL4030_H_
#define __TWL4030_H_
+#include <linux/types.h>
+#include <linux/input/matrix_keypad.h>
+
/*
* Using the twl4030 core we address registers using a pair
* { module id, relative register offset }
@@ -302,13 +305,17 @@ struct twl4030_madc_platform_data {
int irq_line;
};
+/* Boards have uniqe mappings of {col, row} --> keycode.
+ * Column and row are 4 bits, but range only from 0..7.
+ * a PERSISTENT_KEY is "always on" and never reported.
+ */
+#define PERSISTENT_KEY(c, r) KEY((c), (r), KEY_RESERVED)
+
struct twl4030_keypad_data {
- int rows;
- int cols;
- int *keymap;
- int irq;
- unsigned int keymapsize;
- unsigned int rep:1;
+ const struct matrix_keymap_data *keymap_data;
+ unsigned rows;
+ unsigned cols;
+ bool rep;
};
enum twl4030_usb_mode {
diff --git a/include/linux/input/eeti_ts.h b/include/linux/input/eeti_ts.h
new file mode 100644
index 00000000000..f875b316249
--- /dev/null
+++ b/include/linux/input/eeti_ts.h
@@ -0,0 +1,9 @@
+#ifndef LINUX_INPUT_EETI_TS_H
+#define LINUX_INPUT_EETI_TS_H
+
+struct eeti_ts_platform_data {
+ unsigned int irq_active_high;
+};
+
+#endif /* LINUX_INPUT_EETI_TS_H */
+
diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h
index 15d5903af2d..b3cd42d50e1 100644
--- a/include/linux/input/matrix_keypad.h
+++ b/include/linux/input/matrix_keypad.h
@@ -63,4 +63,36 @@ struct matrix_keypad_platform_data {
bool wakeup;
};
+/**
+ * matrix_keypad_build_keymap - convert platform keymap into matrix keymap
+ * @keymap_data: keymap supplied by the platform code
+ * @row_shift: number of bits to shift row value by to advance to the next
+ * line in the keymap
+ * @keymap: expanded version of keymap that is suitable for use by
+ * matrix keyboad driver
+ * @keybit: pointer to bitmap of keys supported by input device
+ *
+ * This function converts platform keymap (encoded with KEY() macro) into
+ * an array of keycodes that is suitable for using in a standard matrix
+ * keyboard driver that uses row and col as indices.
+ */
+static inline void
+matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data,
+ unsigned int row_shift,
+ unsigned short *keymap, unsigned long *keybit)
+{
+ int i;
+
+ for (i = 0; i < keymap_data->keymap_size; i++) {
+ unsigned int key = keymap_data->keymap[i];
+ unsigned int row = KEY_ROW(key);
+ unsigned int col = KEY_COL(key);
+ unsigned short code = KEY_VAL(key);
+
+ keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code;
+ __set_bit(code, keybit);
+ }
+ __clear_bit(KEY_RESERVED, keybit);
+}
+
#endif /* _MATRIX_KEYPAD_H */
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 1ac57e522a1..8e9e151f811 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -348,6 +348,7 @@ enum
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
+ BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index 3db5d8d3748..f8f8900fc5e 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -14,7 +14,7 @@
#define KVM_API_VERSION 12
-/* for KVM_TRACE_ENABLE */
+/* for KVM_TRACE_ENABLE, deprecated */
struct kvm_user_trace_setup {
__u32 buf_size; /* sub_buffer size of each per-cpu */
__u32 buf_nr; /* the number of sub_buffers of each per-cpu */
@@ -70,6 +70,14 @@ struct kvm_irqchip {
} chip;
};
+/* for KVM_CREATE_PIT2 */
+struct kvm_pit_config {
+ __u32 flags;
+ __u32 pad[15];
+};
+
+#define KVM_PIT_SPEAKER_DUMMY 1
+
#define KVM_EXIT_UNKNOWN 0
#define KVM_EXIT_EXCEPTION 1
#define KVM_EXIT_IO 2
@@ -87,6 +95,10 @@ struct kvm_irqchip {
#define KVM_EXIT_S390_RESET 14
#define KVM_EXIT_DCR 15
#define KVM_EXIT_NMI 16
+#define KVM_EXIT_INTERNAL_ERROR 17
+
+/* For KVM_EXIT_INTERNAL_ERROR */
+#define KVM_INTERNAL_ERROR_EMULATION 1
/* for KVM_RUN, returned by mmap(vcpu_fd, offset=0) */
struct kvm_run {
@@ -173,6 +185,9 @@ struct kvm_run {
__u32 data;
__u8 is_write;
} dcr;
+ struct {
+ __u32 suberror;
+ } internal;
/* Fix the size of the union. */
char padding[256];
};
@@ -292,6 +307,28 @@ struct kvm_guest_debug {
struct kvm_guest_debug_arch arch;
};
+enum {
+ kvm_ioeventfd_flag_nr_datamatch,
+ kvm_ioeventfd_flag_nr_pio,
+ kvm_ioeventfd_flag_nr_deassign,
+ kvm_ioeventfd_flag_nr_max,
+};
+
+#define KVM_IOEVENTFD_FLAG_DATAMATCH (1 << kvm_ioeventfd_flag_nr_datamatch)
+#define KVM_IOEVENTFD_FLAG_PIO (1 << kvm_ioeventfd_flag_nr_pio)
+#define KVM_IOEVENTFD_FLAG_DEASSIGN (1 << kvm_ioeventfd_flag_nr_deassign)
+
+#define KVM_IOEVENTFD_VALID_FLAG_MASK ((1 << kvm_ioeventfd_flag_nr_max) - 1)
+
+struct kvm_ioeventfd {
+ __u64 datamatch;
+ __u64 addr; /* legal pio/mmio address */
+ __u32 len; /* 1, 2, 4, or 8 bytes */
+ __s32 fd;
+ __u32 flags;
+ __u8 pad[36];
+};
+
#define KVM_TRC_SHIFT 16
/*
* kvm trace categories
@@ -310,35 +347,6 @@ struct kvm_guest_debug {
#define KVM_TRC_CYCLE_SIZE 8
#define KVM_TRC_EXTRA_MAX 7
-/* This structure represents a single trace buffer record. */
-struct kvm_trace_rec {
- /* variable rec_val
- * is split into:
- * bits 0 - 27 -> event id
- * bits 28 -30 -> number of extra data args of size u32
- * bits 31 -> binary indicator for if tsc is in record
- */
- __u32 rec_val;
- __u32 pid;
- __u32 vcpu_id;
- union {
- struct {
- __u64 timestamp;
- __u32 extra_u32[KVM_TRC_EXTRA_MAX];
- } __attribute__((packed)) timestamp;
- struct {
- __u32 extra_u32[KVM_TRC_EXTRA_MAX];
- } notimestamp;
- } u;
-};
-
-#define TRACE_REC_EVENT_ID(val) \
- (0x0fffffff & (val))
-#define TRACE_REC_NUM_DATA_ARGS(val) \
- (0x70000000 & ((val) << 28))
-#define TRACE_REC_TCS(val) \
- (0x80000000 & ((val) << 31))
-
#define KVMIO 0xAE
/*
@@ -415,6 +423,19 @@ struct kvm_trace_rec {
#define KVM_CAP_ASSIGN_DEV_IRQ 29
/* Another bug in KVM_SET_USER_MEMORY_REGION fixed: */
#define KVM_CAP_JOIN_MEMORY_REGIONS_WORKS 30
+#ifdef __KVM_HAVE_MCE
+#define KVM_CAP_MCE 31
+#endif
+#define KVM_CAP_IRQFD 32
+#ifdef __KVM_HAVE_PIT
+#define KVM_CAP_PIT2 33
+#endif
+#define KVM_CAP_SET_BOOT_CPU_ID 34
+#ifdef __KVM_HAVE_PIT_STATE2
+#define KVM_CAP_PIT_STATE2 35
+#endif
+#define KVM_CAP_IOEVENTFD 36
+#define KVM_CAP_SET_IDENTITY_MAP_ADDR 37
#ifdef KVM_CAP_IRQ_ROUTING
@@ -454,15 +475,32 @@ struct kvm_irq_routing {
#endif
+#ifdef KVM_CAP_MCE
+/* x86 MCE */
+struct kvm_x86_mce {
+ __u64 status;
+ __u64 addr;
+ __u64 misc;
+ __u64 mcg_status;
+ __u8 bank;
+ __u8 pad1[7];
+ __u64 pad2[3];
+};
+#endif
+
+#define KVM_IRQFD_FLAG_DEASSIGN (1 << 0)
+
+struct kvm_irqfd {
+ __u32 fd;
+ __u32 gsi;
+ __u32 flags;
+ __u8 pad[20];
+};
+
/*
* ioctls for VM fds
*/
#define KVM_SET_MEMORY_REGION _IOW(KVMIO, 0x40, struct kvm_memory_region)
-#define KVM_SET_NR_MMU_PAGES _IO(KVMIO, 0x44)
-#define KVM_GET_NR_MMU_PAGES _IO(KVMIO, 0x45)
-#define KVM_SET_USER_MEMORY_REGION _IOW(KVMIO, 0x46,\
- struct kvm_userspace_memory_region)
-#define KVM_SET_TSS_ADDR _IO(KVMIO, 0x47)
/*
* KVM_CREATE_VCPU receives as a parameter the vcpu slot, and returns
* a vcpu fd.
@@ -470,6 +508,12 @@ struct kvm_irq_routing {
#define KVM_CREATE_VCPU _IO(KVMIO, 0x41)
#define KVM_GET_DIRTY_LOG _IOW(KVMIO, 0x42, struct kvm_dirty_log)
#define KVM_SET_MEMORY_ALIAS _IOW(KVMIO, 0x43, struct kvm_memory_alias)
+#define KVM_SET_NR_MMU_PAGES _IO(KVMIO, 0x44)
+#define KVM_GET_NR_MMU_PAGES _IO(KVMIO, 0x45)
+#define KVM_SET_USER_MEMORY_REGION _IOW(KVMIO, 0x46,\
+ struct kvm_userspace_memory_region)
+#define KVM_SET_TSS_ADDR _IO(KVMIO, 0x47)
+#define KVM_SET_IDENTITY_MAP_ADDR _IOW(KVMIO, 0x48, __u64)
/* Device model IOC */
#define KVM_CREATE_IRQCHIP _IO(KVMIO, 0x60)
#define KVM_IRQ_LINE _IOW(KVMIO, 0x61, struct kvm_irq_level)
@@ -498,6 +542,10 @@ struct kvm_irq_routing {
#define KVM_ASSIGN_SET_MSIX_ENTRY \
_IOW(KVMIO, 0x74, struct kvm_assigned_msix_entry)
#define KVM_DEASSIGN_DEV_IRQ _IOW(KVMIO, 0x75, struct kvm_assigned_irq)
+#define KVM_IRQFD _IOW(KVMIO, 0x76, struct kvm_irqfd)
+#define KVM_CREATE_PIT2 _IOW(KVMIO, 0x77, struct kvm_pit_config)
+#define KVM_SET_BOOT_CPU_ID _IO(KVMIO, 0x78)
+#define KVM_IOEVENTFD _IOW(KVMIO, 0x79, struct kvm_ioeventfd)
/*
* ioctls for vcpu fds
@@ -541,6 +589,10 @@ struct kvm_irq_routing {
#define KVM_NMI _IO(KVMIO, 0x9a)
/* Available with KVM_CAP_SET_GUEST_DEBUG */
#define KVM_SET_GUEST_DEBUG _IOW(KVMIO, 0x9b, struct kvm_guest_debug)
+/* MCE for x86 */
+#define KVM_X86_SETUP_MCE _IOW(KVMIO, 0x9c, __u64)
+#define KVM_X86_GET_MCE_CAP_SUPPORTED _IOR(KVMIO, 0x9d, __u64)
+#define KVM_X86_SET_MCE _IOW(KVMIO, 0x9e, struct kvm_x86_mce)
/*
* Deprecated interfaces
@@ -563,6 +615,9 @@ struct kvm_debug_guest {
#define KVM_IA64_VCPU_GET_STACK _IOR(KVMIO, 0x9a, void *)
#define KVM_IA64_VCPU_SET_STACK _IOW(KVMIO, 0x9b, void *)
+#define KVM_GET_PIT2 _IOR(KVMIO, 0x9f, struct kvm_pit_state2)
+#define KVM_SET_PIT2 _IOW(KVMIO, 0xa0, struct kvm_pit_state2)
+
#define KVM_TRC_INJ_VIRQ (KVM_TRC_HANDLER + 0x02)
#define KVM_TRC_REDELIVER_EVT (KVM_TRC_HANDLER + 0x03)
#define KVM_TRC_PEND_INTR (KVM_TRC_HANDLER + 0x04)
@@ -633,7 +688,7 @@ struct kvm_assigned_msix_nr {
__u16 padding;
};
-#define KVM_MAX_MSIX_PER_DEV 512
+#define KVM_MAX_MSIX_PER_DEV 256
struct kvm_assigned_msix_entry {
__u32 assigned_dev_id;
__u32 gsi;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 3060bdc35ff..4af56036a6b 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -42,6 +42,7 @@
#define KVM_USERSPACE_IRQ_SOURCE_ID 0
+struct kvm;
struct kvm_vcpu;
extern struct kmem_cache *kvm_vcpu_cache;
@@ -59,10 +60,18 @@ struct kvm_io_bus {
void kvm_io_bus_init(struct kvm_io_bus *bus);
void kvm_io_bus_destroy(struct kvm_io_bus *bus);
-struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus,
- gpa_t addr, int len, int is_write);
-void kvm_io_bus_register_dev(struct kvm_io_bus *bus,
- struct kvm_io_device *dev);
+int kvm_io_bus_write(struct kvm_io_bus *bus, gpa_t addr, int len,
+ const void *val);
+int kvm_io_bus_read(struct kvm_io_bus *bus, gpa_t addr, int len,
+ void *val);
+int __kvm_io_bus_register_dev(struct kvm_io_bus *bus,
+ struct kvm_io_device *dev);
+int kvm_io_bus_register_dev(struct kvm *kvm, struct kvm_io_bus *bus,
+ struct kvm_io_device *dev);
+void __kvm_io_bus_unregister_dev(struct kvm_io_bus *bus,
+ struct kvm_io_device *dev);
+void kvm_io_bus_unregister_dev(struct kvm *kvm, struct kvm_io_bus *bus,
+ struct kvm_io_device *dev);
struct kvm_vcpu {
struct kvm *kvm;
@@ -103,7 +112,7 @@ struct kvm_memory_slot {
struct {
unsigned long rmap_pde;
int write_count;
- } *lpage_info;
+ } *lpage_info[KVM_NR_PAGE_SIZES - 1];
unsigned long userspace_addr;
int user_alloc;
};
@@ -124,7 +133,6 @@ struct kvm_kernel_irq_routing_entry {
};
struct kvm {
- struct mutex lock; /* protects the vcpus array and APIC accesses */
spinlock_t mmu_lock;
spinlock_t requests_lock;
struct rw_semaphore slots_lock;
@@ -132,10 +140,23 @@ struct kvm {
int nmemslots;
struct kvm_memory_slot memslots[KVM_MEMORY_SLOTS +
KVM_PRIVATE_MEM_SLOTS];
+#ifdef CONFIG_KVM_APIC_ARCHITECTURE
+ u32 bsp_vcpu_id;
+ struct kvm_vcpu *bsp_vcpu;
+#endif
struct kvm_vcpu *vcpus[KVM_MAX_VCPUS];
+ atomic_t online_vcpus;
struct list_head vm_list;
+ struct mutex lock;
struct kvm_io_bus mmio_bus;
struct kvm_io_bus pio_bus;
+#ifdef CONFIG_HAVE_KVM_EVENTFD
+ struct {
+ spinlock_t lock;
+ struct list_head items;
+ } irqfds;
+ struct list_head ioeventfds;
+#endif
struct kvm_vm_stat stat;
struct kvm_arch arch;
atomic_t users_count;
@@ -144,6 +165,7 @@ struct kvm {
struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
#endif
+ struct mutex irq_lock;
#ifdef CONFIG_HAVE_KVM_IRQCHIP
struct list_head irq_routing; /* of kvm_kernel_irq_routing_entry */
struct hlist_head mask_notifier_list;
@@ -167,6 +189,17 @@ struct kvm {
#define kvm_printf(kvm, fmt ...) printk(KERN_DEBUG fmt)
#define vcpu_printf(vcpu, fmt...) kvm_printf(vcpu->kvm, fmt)
+static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i)
+{
+ smp_rmb();
+ return kvm->vcpus[i];
+}
+
+#define kvm_for_each_vcpu(idx, vcpup, kvm) \
+ for (idx = 0, vcpup = kvm_get_vcpu(kvm, idx); \
+ idx < atomic_read(&kvm->online_vcpus) && vcpup; \
+ vcpup = kvm_get_vcpu(kvm, ++idx))
+
int kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id);
void kvm_vcpu_uninit(struct kvm_vcpu *vcpu);
@@ -201,6 +234,7 @@ int kvm_arch_set_memory_region(struct kvm *kvm,
struct kvm_userspace_memory_region *mem,
struct kvm_memory_slot old,
int user_alloc);
+void kvm_disable_largepages(void);
void kvm_arch_flush_shadow(struct kvm *kvm);
gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn);
struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn);
@@ -243,8 +277,6 @@ long kvm_arch_dev_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg);
long kvm_arch_vcpu_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg);
-void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu);
-void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu);
int kvm_dev_ioctl_check_extension(long ext);
@@ -300,7 +332,6 @@ int kvm_arch_hardware_setup(void);
void kvm_arch_hardware_unsetup(void);
void kvm_arch_check_processor_compat(void *rtn);
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu);
-int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu);
void kvm_free_physmem(struct kvm *kvm);
@@ -309,8 +340,6 @@ void kvm_arch_destroy_vm(struct kvm *kvm);
void kvm_free_all_assigned_devices(struct kvm *kvm);
void kvm_arch_sync_events(struct kvm *kvm);
-int kvm_cpu_get_interrupt(struct kvm_vcpu *v);
-int kvm_cpu_has_interrupt(struct kvm_vcpu *v);
int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu);
void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
@@ -366,7 +395,8 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, int irq, int level);
void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin);
void kvm_register_irq_ack_notifier(struct kvm *kvm,
struct kvm_irq_ack_notifier *kian);
-void kvm_unregister_irq_ack_notifier(struct kvm_irq_ack_notifier *kian);
+void kvm_unregister_irq_ack_notifier(struct kvm *kvm,
+ struct kvm_irq_ack_notifier *kian);
int kvm_request_irq_source_id(struct kvm *kvm);
void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id);
@@ -459,37 +489,6 @@ struct kvm_stats_debugfs_item {
extern struct kvm_stats_debugfs_item debugfs_entries[];
extern struct dentry *kvm_debugfs_dir;
-#define KVMTRACE_5D(evt, vcpu, d1, d2, d3, d4, d5, name) \
- trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
- vcpu, 5, d1, d2, d3, d4, d5)
-#define KVMTRACE_4D(evt, vcpu, d1, d2, d3, d4, name) \
- trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
- vcpu, 4, d1, d2, d3, d4, 0)
-#define KVMTRACE_3D(evt, vcpu, d1, d2, d3, name) \
- trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
- vcpu, 3, d1, d2, d3, 0, 0)
-#define KVMTRACE_2D(evt, vcpu, d1, d2, name) \
- trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
- vcpu, 2, d1, d2, 0, 0, 0)
-#define KVMTRACE_1D(evt, vcpu, d1, name) \
- trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
- vcpu, 1, d1, 0, 0, 0, 0)
-#define KVMTRACE_0D(evt, vcpu, name) \
- trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
- vcpu, 0, 0, 0, 0, 0, 0)
-
-#ifdef CONFIG_KVM_TRACE
-int kvm_trace_ioctl(unsigned int ioctl, unsigned long arg);
-void kvm_trace_cleanup(void);
-#else
-static inline
-int kvm_trace_ioctl(unsigned int ioctl, unsigned long arg)
-{
- return -EINVAL;
-}
-#define kvm_trace_cleanup() ((void)0)
-#endif
-
#ifdef KVM_ARCH_WANT_MMU_NOTIFIER
static inline int mmu_notifier_retry(struct kvm_vcpu *vcpu, unsigned long mmu_seq)
{
@@ -525,4 +524,33 @@ static inline void kvm_free_irq_routing(struct kvm *kvm) {}
#endif
+#ifdef CONFIG_HAVE_KVM_EVENTFD
+
+void kvm_eventfd_init(struct kvm *kvm);
+int kvm_irqfd(struct kvm *kvm, int fd, int gsi, int flags);
+void kvm_irqfd_release(struct kvm *kvm);
+int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args);
+
+#else
+
+static inline void kvm_eventfd_init(struct kvm *kvm) {}
+static inline int kvm_irqfd(struct kvm *kvm, int fd, int gsi, int flags)
+{
+ return -EINVAL;
+}
+
+static inline void kvm_irqfd_release(struct kvm *kvm) {}
+static inline int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
+{
+ return -ENOSYS;
+}
+
+#endif /* CONFIG_HAVE_KVM_EVENTFD */
+
+#ifdef CONFIG_KVM_APIC_ARCHITECTURE
+static inline bool kvm_vcpu_is_bsp(struct kvm_vcpu *vcpu)
+{
+ return vcpu->kvm->bsp_vcpu_id == vcpu->vcpu_id;
+}
+#endif
#endif
diff --git a/include/linux/kvm_para.h b/include/linux/kvm_para.h
index 3ddce03766c..d73109243fd 100644
--- a/include/linux/kvm_para.h
+++ b/include/linux/kvm_para.h
@@ -13,6 +13,7 @@
#define KVM_ENOSYS 1000
#define KVM_EFAULT EFAULT
#define KVM_E2BIG E2BIG
+#define KVM_EPERM EPERM
#define KVM_HC_VAPIC_POLL_IRQ 1
#define KVM_HC_MMU_OP 2
diff --git a/include/linux/libps2.h b/include/linux/libps2.h
index b94534b7e26..fcf5fbe6a50 100644
--- a/include/linux/libps2.h
+++ b/include/linux/libps2.h
@@ -44,6 +44,7 @@ struct ps2dev {
void ps2_init(struct ps2dev *ps2dev, struct serio *serio);
int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout);
void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout);
+int __ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command);
int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command);
int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data);
int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data);
diff --git a/include/linux/serio.h b/include/linux/serio.h
index 126d24c9eaa..a640bc2afe7 100644
--- a/include/linux/serio.h
+++ b/include/linux/serio.h
@@ -31,8 +31,6 @@ struct serio {
bool manual_bind;
bool registered; /* port has been fully registered with driver core */
- bool suspended; /* port is suspended */
-
struct serio_device_id id;
diff --git a/include/linux/slob_def.h b/include/linux/slob_def.h
index bb5368df4be..0ec00b39d00 100644
--- a/include/linux/slob_def.h
+++ b/include/linux/slob_def.h
@@ -34,9 +34,4 @@ static __always_inline void *__kmalloc(size_t size, gfp_t flags)
return kmalloc(size, flags);
}
-static inline void kmem_cache_init_late(void)
-{
- /* Nothing to do */
-}
-
#endif /* __LINUX_SLOB_DEF_H */
diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h
index c1c862b1d01..5ad70a60fd7 100644
--- a/include/linux/slub_def.h
+++ b/include/linux/slub_def.h
@@ -153,12 +153,10 @@ static __always_inline int kmalloc_index(size_t size)
if (size <= KMALLOC_MIN_SIZE)
return KMALLOC_SHIFT_LOW;
-#if KMALLOC_MIN_SIZE <= 64
- if (size > 64 && size <= 96)
+ if (KMALLOC_MIN_SIZE <= 32 && size > 64 && size <= 96)
return 1;
- if (size > 128 && size <= 192)
+ if (KMALLOC_MIN_SIZE <= 64 && size > 128 && size <= 192)
return 2;
-#endif
if (size <= 8) return 3;
if (size <= 16) return 4;
if (size <= 32) return 5;
@@ -304,6 +302,4 @@ static __always_inline void *kmalloc_node(size_t size, gfp_t flags, int node)
}
#endif
-void __init kmem_cache_init_late(void);
-
#endif /* _LINUX_SLUB_DEF_H */
diff --git a/include/linux/tty.h b/include/linux/tty.h
index 0d3974f59c5..a916a318004 100644
--- a/include/linux/tty.h
+++ b/include/linux/tty.h
@@ -519,10 +519,6 @@ extern void serial_console_init(void);
extern int pcxe_open(struct tty_struct *tty, struct file *filp);
-/* printk.c */
-
-extern void console_print(const char *);
-
/* vt.c */
extern int vt_ioctl(struct tty_struct *tty, struct file *file,
diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h
index 6f69968eab2..0c9878123d5 100644
--- a/include/linux/wm97xx.h
+++ b/include/linux/wm97xx.h
@@ -16,6 +16,12 @@
#include <linux/platform_device.h>
/*
+ * WM97xx variants
+ */
+#define WM97xx_GENERIC 0x0000
+#define WM97xx_WM1613 0x1613
+
+/*
* WM97xx AC97 Touchscreen registers
*/
#define AC97_WM97XX_DIGITISER1 0x76
@@ -283,6 +289,7 @@ struct wm97xx {
unsigned pen_is_down:1; /* Pen is down */
unsigned aux_waiting:1; /* aux measurement waiting */
unsigned pen_probably_down:1; /* used in polling mode */
+ u16 variant; /* WM97xx chip variant */
u16 suspend_mode; /* PRP in suspend mode */
};
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index 78b1e4684cc..d347632f186 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -150,10 +150,6 @@ int write_cache_pages(struct address_space *mapping,
struct writeback_control *wbc, writepage_t writepage,
void *data);
int do_writepages(struct address_space *mapping, struct writeback_control *wbc);
-int sync_page_range(struct inode *inode, struct address_space *mapping,
- loff_t pos, loff_t count);
-int sync_page_range_nolock(struct inode *inode, struct address_space *mapping,
- loff_t pos, loff_t count);
void set_page_dirty_balance(struct page *page, int page_mkwrite);
void writeback_set_ratelimit(void);
diff --git a/include/scsi/fc/fc_gs.h b/include/scsi/fc/fc_gs.h
index ffab0272c65..324dd0e3c62 100644
--- a/include/scsi/fc/fc_gs.h
+++ b/include/scsi/fc/fc_gs.h
@@ -87,6 +87,7 @@ enum fc_ct_explan {
FC_FS_EXP_PNAM = 0x02, /* port name not registered */
FC_FS_EXP_NNAM = 0x03, /* node name not registered */
FC_FS_EXP_COS = 0x04, /* class of service not registered */
+ FC_FS_EXP_FTNR = 0x07, /* FC-4 types not registered */
/* definitions not complete */
};
diff --git a/include/scsi/fc_encode.h b/include/scsi/fc_encode.h
index a0ff61c3e93..27dad703824 100644
--- a/include/scsi/fc_encode.h
+++ b/include/scsi/fc_encode.h
@@ -32,6 +32,7 @@ struct fc_ct_req {
struct fc_ns_gid_ft gid;
struct fc_ns_rn_id rn;
struct fc_ns_rft rft;
+ struct fc_ns_fid fid;
} payload;
};
@@ -57,6 +58,23 @@ static inline void fc_fill_fc_hdr(struct fc_frame *fp, enum fc_rctl r_ctl,
}
/**
+ * fc_adisc_fill() - Fill in adisc request frame
+ * @lport: local port.
+ * @fp: fc frame where payload will be placed.
+ */
+static inline void fc_adisc_fill(struct fc_lport *lport, struct fc_frame *fp)
+{
+ struct fc_els_adisc *adisc;
+
+ adisc = fc_frame_payload_get(fp, sizeof(*adisc));
+ memset(adisc, 0, sizeof(*adisc));
+ adisc->adisc_cmd = ELS_ADISC;
+ put_unaligned_be64(lport->wwpn, &adisc->adisc_wwpn);
+ put_unaligned_be64(lport->wwnn, &adisc->adisc_wwnn);
+ hton24(adisc->adisc_port_id, fc_host_port_id(lport->host));
+}
+
+/**
* fc_ct_hdr_fill- fills ct header and reset ct payload
* returns pointer to ct request.
*/
@@ -77,10 +95,17 @@ static inline struct fc_ct_req *fc_ct_hdr_fill(const struct fc_frame *fp,
}
/**
- * fc_ct_fill - Fill in a name service request frame
+ * fc_ct_fill() - Fill in a name service request frame
+ * @lport: local port.
+ * @fc_id: FC_ID of non-destination rport for GPN_ID and similar inquiries.
+ * @fp: frame to contain payload.
+ * @op: CT opcode.
+ * @r_ctl: pointer to FC header R_CTL.
+ * @fh_type: pointer to FC-4 type.
*/
-static inline int fc_ct_fill(struct fc_lport *lport, struct fc_frame *fp,
- unsigned int op, enum fc_rctl *r_ctl, u32 *did,
+static inline int fc_ct_fill(struct fc_lport *lport,
+ u32 fc_id, struct fc_frame *fp,
+ unsigned int op, enum fc_rctl *r_ctl,
enum fc_fh_type *fh_type)
{
struct fc_ct_req *ct;
@@ -91,6 +116,11 @@ static inline int fc_ct_fill(struct fc_lport *lport, struct fc_frame *fp,
ct->payload.gid.fn_fc4_type = FC_TYPE_FCP;
break;
+ case FC_NS_GPN_ID:
+ ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_fid));
+ hton24(ct->payload.fid.fp_fid, fc_id);
+ break;
+
case FC_NS_RFT_ID:
ct = fc_ct_hdr_fill(fp, op, sizeof(struct fc_ns_rft));
hton24(ct->payload.rft.fid.fp_fid,
@@ -110,7 +140,6 @@ static inline int fc_ct_fill(struct fc_lport *lport, struct fc_frame *fp,
return -EINVAL;
}
*r_ctl = FC_RCTL_DD_UNSOL_CTL;
- *did = FC_FID_DIR_SERV;
*fh_type = FC_TYPE_CT;
return 0;
}
@@ -249,51 +278,42 @@ static inline void fc_scr_fill(struct fc_lport *lport, struct fc_frame *fp)
/**
* fc_els_fill - Fill in an ELS request frame
*/
-static inline int fc_els_fill(struct fc_lport *lport, struct fc_rport *rport,
+static inline int fc_els_fill(struct fc_lport *lport,
+ u32 did,
struct fc_frame *fp, unsigned int op,
- enum fc_rctl *r_ctl, u32 *did, enum fc_fh_type *fh_type)
+ enum fc_rctl *r_ctl, enum fc_fh_type *fh_type)
{
switch (op) {
+ case ELS_ADISC:
+ fc_adisc_fill(lport, fp);
+ break;
+
case ELS_PLOGI:
fc_plogi_fill(lport, fp, ELS_PLOGI);
- *did = rport->port_id;
break;
case ELS_FLOGI:
fc_flogi_fill(lport, fp);
- *did = FC_FID_FLOGI;
break;
case ELS_LOGO:
fc_logo_fill(lport, fp);
- *did = FC_FID_FLOGI;
- /*
- * if rport is valid then it
- * is port logo, therefore
- * set did to rport id.
- */
- if (rport)
- *did = rport->port_id;
break;
case ELS_RTV:
fc_rtv_fill(lport, fp);
- *did = rport->port_id;
break;
case ELS_REC:
fc_rec_fill(lport, fp);
- *did = rport->port_id;
break;
case ELS_PRLI:
fc_prli_fill(lport, fp);
- *did = rport->port_id;
break;
case ELS_SCR:
fc_scr_fill(lport, fp);
- *did = FC_FID_FCTRL;
break;
default:
diff --git a/include/scsi/fc_frame.h b/include/scsi/fc_frame.h
index 59511057cee..c35d2383cc2 100644
--- a/include/scsi/fc_frame.h
+++ b/include/scsi/fc_frame.h
@@ -37,13 +37,6 @@
#define FC_FRAME_HEADROOM 32 /* headroom for VLAN + FCoE headers */
#define FC_FRAME_TAILROOM 8 /* trailer space for FCoE */
-/*
- * Information about an individual fibre channel frame received or to be sent.
- * The buffer may be in up to 4 additional non-contiguous sections,
- * but the linear section must hold the frame header.
- */
-#define FC_FRAME_SG_LEN 4 /* scatter/gather list maximum length */
-
#define fp_skb(fp) (&((fp)->skb))
#define fr_hdr(fp) ((fp)->skb.data)
#define fr_len(fp) ((fp)->skb.len)
diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h
index 4426f00da5f..d67dda2b6aa 100644
--- a/include/scsi/iscsi_if.h
+++ b/include/scsi/iscsi_if.h
@@ -262,6 +262,7 @@ enum iscsi_err {
ISCSI_ERR_NO_SCSI_CMD = ISCSI_ERR_BASE + 17,
ISCSI_ERR_INVALID_HOST = ISCSI_ERR_BASE + 18,
ISCSI_ERR_XMIT_FAILED = ISCSI_ERR_BASE + 19,
+ ISCSI_ERR_TCP_CONN_CLOSE = ISCSI_ERR_BASE + 20,
};
/*
diff --git a/include/scsi/libfc.h b/include/scsi/libfc.h
index b92584a8843..65dc9aacbf7 100644
--- a/include/scsi/libfc.h
+++ b/include/scsi/libfc.h
@@ -51,55 +51,49 @@ do { \
do { \
CMD; \
} while (0); \
-} while (0);
+} while (0)
#define FC_LIBFC_DBG(fmt, args...) \
FC_CHECK_LOGGING(FC_LIBFC_LOGGING, \
- printk(KERN_INFO "libfc: " fmt, ##args);)
+ printk(KERN_INFO "libfc: " fmt, ##args))
#define FC_LPORT_DBG(lport, fmt, args...) \
FC_CHECK_LOGGING(FC_LPORT_LOGGING, \
- printk(KERN_INFO "lport: %6x: " fmt, \
- fc_host_port_id(lport->host), ##args);)
+ printk(KERN_INFO "host%u: lport %6x: " fmt, \
+ (lport)->host->host_no, \
+ fc_host_port_id((lport)->host), ##args))
#define FC_DISC_DBG(disc, fmt, args...) \
FC_CHECK_LOGGING(FC_DISC_LOGGING, \
- printk(KERN_INFO "disc: %6x: " fmt, \
- fc_host_port_id(disc->lport->host), \
- ##args);)
-
-#define FC_RPORT_DBG(rport, fmt, args...) \
-do { \
- struct fc_rport_libfc_priv *rdata = rport->dd_data; \
- struct fc_lport *lport = rdata->local_port; \
+ printk(KERN_INFO "host%u: disc: " fmt, \
+ (disc)->lport->host->host_no, \
+ ##args))
+
+#define FC_RPORT_ID_DBG(lport, port_id, fmt, args...) \
FC_CHECK_LOGGING(FC_RPORT_LOGGING, \
- printk(KERN_INFO "rport: %6x: %6x: " fmt, \
- fc_host_port_id(lport->host), \
- rport->port_id, ##args);) \
-} while (0);
+ printk(KERN_INFO "host%u: rport %6x: " fmt, \
+ (lport)->host->host_no, \
+ (port_id), ##args))
+
+#define FC_RPORT_DBG(rdata, fmt, args...) \
+ FC_RPORT_ID_DBG((rdata)->local_port, (rdata)->ids.port_id, fmt, ##args)
#define FC_FCP_DBG(pkt, fmt, args...) \
FC_CHECK_LOGGING(FC_FCP_LOGGING, \
- printk(KERN_INFO "fcp: %6x: %6x: " fmt, \
- fc_host_port_id(pkt->lp->host), \
- pkt->rport->port_id, ##args);)
-
-#define FC_EM_DBG(em, fmt, args...) \
- FC_CHECK_LOGGING(FC_EM_LOGGING, \
- printk(KERN_INFO "em: %6x: " fmt, \
- fc_host_port_id(em->lp->host), \
- ##args);)
+ printk(KERN_INFO "host%u: fcp: %6x: " fmt, \
+ (pkt)->lp->host->host_no, \
+ pkt->rport->port_id, ##args))
#define FC_EXCH_DBG(exch, fmt, args...) \
FC_CHECK_LOGGING(FC_EXCH_LOGGING, \
- printk(KERN_INFO "exch: %6x: %4x: " fmt, \
- fc_host_port_id(exch->lp->host), \
- exch->xid, ##args);)
+ printk(KERN_INFO "host%u: xid %4x: " fmt, \
+ (exch)->lp->host->host_no, \
+ exch->xid, ##args))
#define FC_SCSI_DBG(lport, fmt, args...) \
FC_CHECK_LOGGING(FC_SCSI_LOGGING, \
- printk(KERN_INFO "scsi: %6x: " fmt, \
- fc_host_port_id(lport->host), ##args);)
+ printk(KERN_INFO "host%u: scsi: " fmt, \
+ (lport)->host->host_no, ##args))
/*
* libfc error codes
@@ -125,7 +119,7 @@ do { \
* FC HBA status
*/
enum fc_lport_state {
- LPORT_ST_NONE = 0,
+ LPORT_ST_DISABLED = 0,
LPORT_ST_FLOGI,
LPORT_ST_DNS,
LPORT_ST_RPN_ID,
@@ -143,53 +137,74 @@ enum fc_disc_event {
};
enum fc_rport_state {
- RPORT_ST_NONE = 0,
RPORT_ST_INIT, /* initialized */
RPORT_ST_PLOGI, /* waiting for PLOGI completion */
RPORT_ST_PRLI, /* waiting for PRLI completion */
RPORT_ST_RTV, /* waiting for RTV completion */
RPORT_ST_READY, /* ready for use */
RPORT_ST_LOGO, /* port logout sent */
-};
-
-enum fc_rport_trans_state {
- FC_PORTSTATE_ROGUE,
- FC_PORTSTATE_REAL,
+ RPORT_ST_ADISC, /* Discover Address sent */
+ RPORT_ST_DELETE, /* port being deleted */
};
/**
* struct fc_disc_port - temporary discovery port to hold rport identifiers
- * @lp: Fibre Channel host port instance
- * @peers: node for list management during discovery and RSCN processing
- * @ids: identifiers structure to pass to fc_remote_port_add()
- * @rport_work: work struct for starting the rport state machine
+ * @lp: Fibre Channel host port instance
+ * @peers: Node for list management during discovery and RSCN processing
+ * @rport_work: Work struct for starting the rport state machine
+ * @port_id: Port ID of the discovered port
*/
struct fc_disc_port {
struct fc_lport *lp;
struct list_head peers;
- struct fc_rport_identifiers ids;
struct work_struct rport_work;
+ u32 port_id;
};
enum fc_rport_event {
RPORT_EV_NONE = 0,
- RPORT_EV_CREATED,
+ RPORT_EV_READY,
RPORT_EV_FAILED,
RPORT_EV_STOP,
RPORT_EV_LOGO
};
+struct fc_rport_priv;
+
struct fc_rport_operations {
- void (*event_callback)(struct fc_lport *, struct fc_rport *,
+ void (*event_callback)(struct fc_lport *, struct fc_rport_priv *,
enum fc_rport_event);
};
/**
* struct fc_rport_libfc_priv - libfc internal information about a remote port
* @local_port: Fibre Channel host port instance
+ * @rp_state: indicates READY for I/O or DELETE when blocked.
+ * @flags: REC and RETRY supported flags
+ * @e_d_tov: error detect timeout value (in msec)
+ * @r_a_tov: resource allocation timeout value (in msec)
+ */
+struct fc_rport_libfc_priv {
+ struct fc_lport *local_port;
+ enum fc_rport_state rp_state;
+ u16 flags;
+ #define FC_RP_FLAGS_REC_SUPPORTED (1 << 0)
+ #define FC_RP_FLAGS_RETRY (1 << 1)
+ unsigned int e_d_tov;
+ unsigned int r_a_tov;
+};
+
+/**
+ * struct fc_rport_priv - libfc rport and discovery info about a remote port
+ * @local_port: Fibre Channel host port instance
+ * @rport: transport remote port
+ * @kref: reference counter
* @rp_state: state tracks progress of PLOGI, PRLI, and RTV exchanges
+ * @ids: remote port identifiers and roles
* @flags: REC and RETRY supported flags
* @max_seq: maximum number of concurrent sequences
+ * @disc_id: discovery identifier
+ * @maxframe_size: maximum frame size
* @retries: retry count in current state
* @e_d_tov: error detect timeout value (in msec)
* @r_a_tov: resource allocation timeout value (in msec)
@@ -197,38 +212,28 @@ struct fc_rport_operations {
* @retry_work:
* @event_callback: Callback for rport READY, FAILED or LOGO
*/
-struct fc_rport_libfc_priv {
+struct fc_rport_priv {
struct fc_lport *local_port;
+ struct fc_rport *rport;
+ struct kref kref;
enum fc_rport_state rp_state;
+ struct fc_rport_identifiers ids;
u16 flags;
- #define FC_RP_FLAGS_REC_SUPPORTED (1 << 0)
- #define FC_RP_FLAGS_RETRY (1 << 1)
u16 max_seq;
+ u16 disc_id;
+ u16 maxframe_size;
unsigned int retries;
unsigned int e_d_tov;
unsigned int r_a_tov;
- enum fc_rport_trans_state trans_state;
struct mutex rp_mutex;
struct delayed_work retry_work;
enum fc_rport_event event;
struct fc_rport_operations *ops;
struct list_head peers;
struct work_struct event_work;
+ u32 supported_classes;
};
-#define PRIV_TO_RPORT(x) \
- (struct fc_rport *)((void *)x - sizeof(struct fc_rport));
-#define RPORT_TO_PRIV(x) \
- (struct fc_rport_libfc_priv *)((void *)x + sizeof(struct fc_rport));
-
-struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *);
-
-static inline void fc_rport_set_name(struct fc_rport *rport, u64 wwpn, u64 wwnn)
-{
- rport->node_name = wwnn;
- rport->port_name = wwpn;
-}
-
/*
* fcoe stats structure
*/
@@ -344,6 +349,8 @@ static inline bool fc_fcp_is_read(const struct fc_fcp_pkt *fsp)
*/
struct fc_exch_mgr;
+struct fc_exch_mgr_anchor;
+extern u16 fc_cpu_mask; /* cpu mask for possible cpus */
/*
* Sequence.
@@ -368,6 +375,7 @@ struct fc_seq {
*/
struct fc_exch {
struct fc_exch_mgr *em; /* exchange manager */
+ struct fc_exch_pool *pool; /* per cpu exches pool */
u32 state; /* internal driver state */
u16 xid; /* our exchange ID */
struct list_head ex_list; /* free or busy list linkage */
@@ -415,7 +423,7 @@ struct libfc_function_template {
* STATUS: OPTIONAL
*/
struct fc_seq *(*elsct_send)(struct fc_lport *lport,
- struct fc_rport *rport,
+ u32 did,
struct fc_frame *fp,
unsigned int op,
void (*resp)(struct fc_seq *,
@@ -519,25 +527,6 @@ struct libfc_function_template {
void (*exch_done)(struct fc_seq *sp);
/*
- * Assigns a EM and a free XID for an new exchange and then
- * allocates a new exchange and sequence pair.
- * The fp can be used to determine free XID.
- *
- * STATUS: OPTIONAL
- */
- struct fc_exch *(*exch_get)(struct fc_lport *lp, struct fc_frame *fp);
-
- /*
- * Release previously assigned XID by exch_get API.
- * The LLD may implement this if XID is assigned by LLD
- * in exch_get().
- *
- * STATUS: OPTIONAL
- */
- void (*exch_put)(struct fc_lport *lp, struct fc_exch_mgr *mp,
- u16 ex_id);
-
- /*
* Start a new sequence on the same exchange/sequence tuple.
*
* STATUS: OPTIONAL
@@ -577,9 +566,11 @@ struct libfc_function_template {
int (*lport_reset)(struct fc_lport *);
/*
- * Create a remote port
+ * Create a remote port with a given port ID
+ *
+ * STATUS: OPTIONAL
*/
- struct fc_rport *(*rport_create)(struct fc_disc_port *);
+ struct fc_rport_priv *(*rport_create)(struct fc_lport *, u32);
/*
* Initiates the RP state machine. It is called from the LP module.
@@ -592,7 +583,7 @@ struct libfc_function_template {
*
* STATUS: OPTIONAL
*/
- int (*rport_login)(struct fc_rport *rport);
+ int (*rport_login)(struct fc_rport_priv *);
/*
* Logoff, and remove the rport from the transport if
@@ -600,7 +591,7 @@ struct libfc_function_template {
*
* STATUS: OPTIONAL
*/
- int (*rport_logoff)(struct fc_rport *rport);
+ int (*rport_logoff)(struct fc_rport_priv *);
/*
* Recieve a request from a remote port.
@@ -608,14 +599,20 @@ struct libfc_function_template {
* STATUS: OPTIONAL
*/
void (*rport_recv_req)(struct fc_seq *, struct fc_frame *,
- struct fc_rport *);
+ struct fc_lport *);
/*
* lookup an rport by it's port ID.
*
* STATUS: OPTIONAL
*/
- struct fc_rport *(*rport_lookup)(const struct fc_lport *, u32);
+ struct fc_rport_priv *(*rport_lookup)(const struct fc_lport *, u32);
+
+ /*
+ * Destroy an rport after final kref_put().
+ * The argument is a pointer to the kref inside the fc_rport_priv.
+ */
+ void (*rport_destroy)(struct kref *);
/*
* Send a fcp cmd from fsp pkt.
@@ -681,18 +678,16 @@ struct libfc_function_template {
/* information used by the discovery layer */
struct fc_disc {
unsigned char retry_count;
- unsigned char delay;
unsigned char pending;
unsigned char requested;
unsigned short seq_count;
unsigned char buf_len;
- enum fc_disc_event event;
+ u16 disc_id;
void (*disc_callback)(struct fc_lport *,
enum fc_disc_event);
struct list_head rports;
- struct list_head rogue_rports;
struct fc_lport *lport;
struct mutex disc_mutex;
struct fc_gpn_ft_resp partial_buf; /* partial name buffer */
@@ -704,9 +699,9 @@ struct fc_lport {
/* Associations */
struct Scsi_Host *host;
- struct fc_exch_mgr *emp;
- struct fc_rport *dns_rp;
- struct fc_rport *ptp_rp;
+ struct list_head ema_list;
+ struct fc_rport_priv *dns_rp;
+ struct fc_rport_priv *ptp_rp;
void *scsi_priv;
struct fc_disc disc;
@@ -960,6 +955,28 @@ int fc_elsct_init(struct fc_lport *lp);
int fc_exch_init(struct fc_lport *lp);
/*
+ * Adds Exchange Manager (EM) mp to lport.
+ *
+ * Adds specified mp to lport using struct fc_exch_mgr_anchor,
+ * the struct fc_exch_mgr_anchor allows same EM sharing by
+ * more than one lport with their specified match function,
+ * the match function is used in allocating exchange from
+ * added mp.
+ */
+struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport,
+ struct fc_exch_mgr *mp,
+ bool (*match)(struct fc_frame *));
+
+/*
+ * Deletes Exchange Manager (EM) from lport by removing
+ * its anchor ema from lport.
+ *
+ * If removed anchor ema was the last user of its associated EM
+ * then also destroys associated EM.
+ */
+void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema);
+
+/*
* Allocates an Exchange Manager (EM).
*
* The EM manages exchanges for their allocation and
@@ -974,27 +991,25 @@ int fc_exch_init(struct fc_lport *lp);
* a new exchange.
* The LLD may choose to have multiple EMs,
* e.g. one EM instance per CPU receive thread in LLD.
- * The LLD can use exch_get() of struct libfc_function_template
- * to specify XID for a new exchange within
- * a specified EM instance.
*
- * The em_idx to uniquely identify an EM instance.
+ * Specified match function is used in allocating exchanges
+ * from newly allocated EM.
*/
struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
enum fc_class class,
u16 min_xid,
- u16 max_xid);
+ u16 max_xid,
+ bool (*match)(struct fc_frame *));
/*
- * Free an exchange manager.
+ * Free all exchange managers of a lport.
*/
-void fc_exch_mgr_free(struct fc_exch_mgr *mp);
+void fc_exch_mgr_free(struct fc_lport *lport);
/*
* Receive a frame on specified local port and exchange manager.
*/
-void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
- struct fc_frame *fp);
+void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp);
/*
* This function is for exch_seq_send function pointer in
@@ -1036,28 +1051,20 @@ int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec);
void fc_exch_done(struct fc_seq *sp);
/*
- * Assigns a EM and XID for a frame and then allocates
- * a new exchange and sequence pair.
- * The fp can be used to determine free XID.
- */
-struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp);
-
-/*
* Allocate a new exchange and sequence pair.
- * if ex_id is zero then next free exchange id
- * from specified exchange manger mp will be assigned.
*/
-struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
- struct fc_frame *fp, u16 ex_id);
+struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp);
/*
* Start a new sequence on the same exchange as the supplied sequence.
*/
struct fc_seq *fc_seq_start_next(struct fc_seq *sp);
+
/*
- * Reset an exchange manager, completing all sequences and exchanges.
- * If s_id is non-zero, reset only exchanges originating from that FID.
- * If d_id is non-zero, reset only exchanges sending to that FID.
+ * Reset all EMs of a lport, releasing its all sequences and
+ * exchanges. If sid is non-zero, then reset only exchanges
+ * we sourced from that FID. If did is non-zero, reset only
+ * exchanges destined to that FID.
*/
void fc_exch_mgr_reset(struct fc_lport *, u32 s_id, u32 d_id);
@@ -1078,4 +1085,9 @@ void fc_destroy_exch_mgr(void);
int fc_setup_rport(void);
void fc_destroy_rport(void);
+/*
+ * Internal libfc functions.
+ */
+const char *fc_els_resp_type(struct fc_frame *);
+
#endif /* _LIBFC_H_ */
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index 61afeb59a83..887e57e3e22 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -390,6 +390,7 @@ extern void iscsi_session_failure(struct iscsi_session *session,
extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf);
extern void iscsi_suspend_tx(struct iscsi_conn *conn);
+extern void iscsi_suspend_queue(struct iscsi_conn *conn);
extern void iscsi_conn_queue_work(struct iscsi_conn *conn);
#define iscsi_conn_printk(prefix, _c, fmt, a...) \
@@ -415,6 +416,8 @@ extern struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *, itt_t);
extern void iscsi_requeue_task(struct iscsi_task *task);
extern void iscsi_put_task(struct iscsi_task *task);
extern void __iscsi_get_task(struct iscsi_task *task);
+extern void iscsi_complete_scsi_task(struct iscsi_task *task,
+ uint32_t exp_cmdsn, uint32_t max_cmdsn);
/*
* generic helpers
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 3f566af3f10..9af48cbf003 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -187,10 +187,13 @@ struct scsi_device_handler {
void (*detach)(struct scsi_device *);
int (*activate)(struct scsi_device *);
int (*prep_fn)(struct scsi_device *, struct request *);
+ int (*set_params)(struct scsi_device *, const char *);
};
struct scsi_dh_data {
struct scsi_device_handler *scsi_dh;
+ struct scsi_device *sdev;
+ struct kref kref;
char buf[0];
};
diff --git a/include/scsi/scsi_dh.h b/include/scsi/scsi_dh.h
index 33efce20c26..ff2407405b4 100644
--- a/include/scsi/scsi_dh.h
+++ b/include/scsi/scsi_dh.h
@@ -60,6 +60,7 @@ extern int scsi_dh_activate(struct request_queue *);
extern int scsi_dh_handler_exist(const char *);
extern int scsi_dh_attach(struct request_queue *, const char *);
extern void scsi_dh_detach(struct request_queue *);
+extern int scsi_dh_set_params(struct request_queue *, const char *);
#else
static inline int scsi_dh_activate(struct request_queue *req)
{
@@ -77,4 +78,8 @@ static inline void scsi_dh_detach(struct request_queue *q)
{
return;
}
+static inline int scsi_dh_set_params(struct request_queue *req, const char *params)
+{
+ return -SCSI_DH_NOSYS;
+}
#endif
diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h
new file mode 100644
index 00000000000..dbe10845527
--- /dev/null
+++ b/include/trace/events/kvm.h
@@ -0,0 +1,151 @@
+#if !defined(_TRACE_KVM_MAIN_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_KVM_MAIN_H
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM kvm
+#define TRACE_INCLUDE_FILE kvm
+
+#if defined(__KVM_HAVE_IOAPIC)
+TRACE_EVENT(kvm_set_irq,
+ TP_PROTO(unsigned int gsi, int level, int irq_source_id),
+ TP_ARGS(gsi, level, irq_source_id),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, gsi )
+ __field( int, level )
+ __field( int, irq_source_id )
+ ),
+
+ TP_fast_assign(
+ __entry->gsi = gsi;
+ __entry->level = level;
+ __entry->irq_source_id = irq_source_id;
+ ),
+
+ TP_printk("gsi %u level %d source %d",
+ __entry->gsi, __entry->level, __entry->irq_source_id)
+);
+
+#define kvm_deliver_mode \
+ {0x0, "Fixed"}, \
+ {0x1, "LowPrio"}, \
+ {0x2, "SMI"}, \
+ {0x3, "Res3"}, \
+ {0x4, "NMI"}, \
+ {0x5, "INIT"}, \
+ {0x6, "SIPI"}, \
+ {0x7, "ExtINT"}
+
+TRACE_EVENT(kvm_ioapic_set_irq,
+ TP_PROTO(__u64 e, int pin, bool coalesced),
+ TP_ARGS(e, pin, coalesced),
+
+ TP_STRUCT__entry(
+ __field( __u64, e )
+ __field( int, pin )
+ __field( bool, coalesced )
+ ),
+
+ TP_fast_assign(
+ __entry->e = e;
+ __entry->pin = pin;
+ __entry->coalesced = coalesced;
+ ),
+
+ TP_printk("pin %u dst %x vec=%u (%s|%s|%s%s)%s",
+ __entry->pin, (u8)(__entry->e >> 56), (u8)__entry->e,
+ __print_symbolic((__entry->e >> 8 & 0x7), kvm_deliver_mode),
+ (__entry->e & (1<<11)) ? "logical" : "physical",
+ (__entry->e & (1<<15)) ? "level" : "edge",
+ (__entry->e & (1<<16)) ? "|masked" : "",
+ __entry->coalesced ? " (coalesced)" : "")
+);
+
+TRACE_EVENT(kvm_msi_set_irq,
+ TP_PROTO(__u64 address, __u64 data),
+ TP_ARGS(address, data),
+
+ TP_STRUCT__entry(
+ __field( __u64, address )
+ __field( __u64, data )
+ ),
+
+ TP_fast_assign(
+ __entry->address = address;
+ __entry->data = data;
+ ),
+
+ TP_printk("dst %u vec %x (%s|%s|%s%s)",
+ (u8)(__entry->address >> 12), (u8)__entry->data,
+ __print_symbolic((__entry->data >> 8 & 0x7), kvm_deliver_mode),
+ (__entry->address & (1<<2)) ? "logical" : "physical",
+ (__entry->data & (1<<15)) ? "level" : "edge",
+ (__entry->address & (1<<3)) ? "|rh" : "")
+);
+
+#define kvm_irqchips \
+ {KVM_IRQCHIP_PIC_MASTER, "PIC master"}, \
+ {KVM_IRQCHIP_PIC_SLAVE, "PIC slave"}, \
+ {KVM_IRQCHIP_IOAPIC, "IOAPIC"}
+
+TRACE_EVENT(kvm_ack_irq,
+ TP_PROTO(unsigned int irqchip, unsigned int pin),
+ TP_ARGS(irqchip, pin),
+
+ TP_STRUCT__entry(
+ __field( unsigned int, irqchip )
+ __field( unsigned int, pin )
+ ),
+
+ TP_fast_assign(
+ __entry->irqchip = irqchip;
+ __entry->pin = pin;
+ ),
+
+ TP_printk("irqchip %s pin %u",
+ __print_symbolic(__entry->irqchip, kvm_irqchips),
+ __entry->pin)
+);
+
+
+
+#endif /* defined(__KVM_HAVE_IOAPIC) */
+
+#define KVM_TRACE_MMIO_READ_UNSATISFIED 0
+#define KVM_TRACE_MMIO_READ 1
+#define KVM_TRACE_MMIO_WRITE 2
+
+#define kvm_trace_symbol_mmio \
+ { KVM_TRACE_MMIO_READ_UNSATISFIED, "unsatisfied-read" }, \
+ { KVM_TRACE_MMIO_READ, "read" }, \
+ { KVM_TRACE_MMIO_WRITE, "write" }
+
+TRACE_EVENT(kvm_mmio,
+ TP_PROTO(int type, int len, u64 gpa, u64 val),
+ TP_ARGS(type, len, gpa, val),
+
+ TP_STRUCT__entry(
+ __field( u32, type )
+ __field( u32, len )
+ __field( u64, gpa )
+ __field( u64, val )
+ ),
+
+ TP_fast_assign(
+ __entry->type = type;
+ __entry->len = len;
+ __entry->gpa = gpa;
+ __entry->val = val;
+ ),
+
+ TP_printk("mmio %s len %u gpa 0x%llx val 0x%llx",
+ __print_symbolic(__entry->type, kvm_trace_symbol_mmio),
+ __entry->len, __entry->gpa, __entry->val)
+);
+
+#endif /* _TRACE_KVM_MAIN_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/ipc/shm.c b/ipc/shm.c
index 1bc4701ef4f..30162a59621 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -410,7 +410,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
return error;
no_id:
- if (shp->mlock_user) /* shmflg & SHM_HUGETLB case */
+ if (is_file_hugepages(file) && shp->mlock_user)
user_shm_unlock(size, shp->mlock_user);
fput(file);
no_file:
diff --git a/kernel/Makefile b/kernel/Makefile
index b833bd5cc12..961379caf66 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -117,7 +117,7 @@ $(obj)/config_data.gz: .config FORCE
$(call if_changed,gzip)
quiet_cmd_ikconfiggz = IKCFG $@
- cmd_ikconfiggz = (echo "static const char kernel_config_data[] = MAGIC_START"; cat $< | scripts/bin2c; echo "MAGIC_END;") > $@
+ cmd_ikconfiggz = (echo "static const char kernel_config_data[] __used = MAGIC_START"; cat $< | scripts/bin2c; echo "MAGIC_END;") > $@
targets += config_data.h
$(obj)/config_data.h: $(obj)/config_data.gz FORCE
$(call if_changed,ikconfiggz)
diff --git a/kernel/printk.c b/kernel/printk.c
index e10d193a833..602033acd6c 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -1075,12 +1075,6 @@ void __sched console_conditional_schedule(void)
}
EXPORT_SYMBOL(console_conditional_schedule);
-void console_print(const char *s)
-{
- printk(KERN_EMERG "%s", s);
-}
-EXPORT_SYMBOL(console_print);
-
void console_unblank(void)
{
struct console *c;
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 3125cff1c57..6bb59f70740 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -91,6 +91,7 @@ extern int sysctl_nr_trim_pages;
#ifdef CONFIG_RCU_TORTURE_TEST
extern int rcutorture_runnable;
#endif /* #ifdef CONFIG_RCU_TORTURE_TEST */
+extern int blk_iopoll_enabled;
/* Constants used for minimum and maximum */
#ifdef CONFIG_DETECT_SOFTLOCKUP
@@ -997,7 +998,14 @@ static struct ctl_table kern_table[] = {
.proc_handler = &proc_dointvec,
},
#endif
-
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "blk_iopoll",
+ .data = &blk_iopoll_enabled,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_dointvec,
+ },
/*
* NOTE: do not add new entries to this table unless you have read
* Documentation/sysctl/ctl_unnumbered.txt
diff --git a/mm/filemap.c b/mm/filemap.c
index ccea3b665c1..dd51c68e2b8 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -39,11 +39,10 @@
/*
* FIXME: remove all knowledge of the buffer layer from the core VM
*/
-#include <linux/buffer_head.h> /* for generic_osync_inode */
+#include <linux/buffer_head.h> /* for try_to_free_buffers */
#include <asm/mman.h>
-
/*
* Shared mappings implemented 30.11.1994. It's not fully working yet,
* though.
@@ -307,68 +306,24 @@ int wait_on_page_writeback_range(struct address_space *mapping,
}
/**
- * sync_page_range - write and wait on all pages in the passed range
- * @inode: target inode
- * @mapping: target address_space
- * @pos: beginning offset in pages to write
- * @count: number of bytes to write
- *
- * Write and wait upon all the pages in the passed range. This is a "data
- * integrity" operation. It waits upon in-flight writeout before starting and
- * waiting upon new writeout. If there was an IO error, return it.
+ * filemap_fdatawait_range - wait for all under-writeback pages to complete in a given range
+ * @mapping: address space structure to wait for
+ * @start: offset in bytes where the range starts
+ * @end: offset in bytes where the range ends (inclusive)
*
- * We need to re-take i_mutex during the generic_osync_inode list walk because
- * it is otherwise livelockable.
- */
-int sync_page_range(struct inode *inode, struct address_space *mapping,
- loff_t pos, loff_t count)
-{
- pgoff_t start = pos >> PAGE_CACHE_SHIFT;
- pgoff_t end = (pos + count - 1) >> PAGE_CACHE_SHIFT;
- int ret;
-
- if (!mapping_cap_writeback_dirty(mapping) || !count)
- return 0;
- ret = filemap_fdatawrite_range(mapping, pos, pos + count - 1);
- if (ret == 0) {
- mutex_lock(&inode->i_mutex);
- ret = generic_osync_inode(inode, mapping, OSYNC_METADATA);
- mutex_unlock(&inode->i_mutex);
- }
- if (ret == 0)
- ret = wait_on_page_writeback_range(mapping, start, end);
- return ret;
-}
-EXPORT_SYMBOL(sync_page_range);
-
-/**
- * sync_page_range_nolock - write & wait on all pages in the passed range without locking
- * @inode: target inode
- * @mapping: target address_space
- * @pos: beginning offset in pages to write
- * @count: number of bytes to write
+ * Walk the list of under-writeback pages of the given address space
+ * in the given range and wait for all of them.
*
- * Note: Holding i_mutex across sync_page_range_nolock() is not a good idea
- * as it forces O_SYNC writers to different parts of the same file
- * to be serialised right until io completion.
+ * This is just a simple wrapper so that callers don't have to convert offsets
+ * to page indexes themselves
*/
-int sync_page_range_nolock(struct inode *inode, struct address_space *mapping,
- loff_t pos, loff_t count)
+int filemap_fdatawait_range(struct address_space *mapping, loff_t start,
+ loff_t end)
{
- pgoff_t start = pos >> PAGE_CACHE_SHIFT;
- pgoff_t end = (pos + count - 1) >> PAGE_CACHE_SHIFT;
- int ret;
-
- if (!mapping_cap_writeback_dirty(mapping) || !count)
- return 0;
- ret = filemap_fdatawrite_range(mapping, pos, pos + count - 1);
- if (ret == 0)
- ret = generic_osync_inode(inode, mapping, OSYNC_METADATA);
- if (ret == 0)
- ret = wait_on_page_writeback_range(mapping, start, end);
- return ret;
+ return wait_on_page_writeback_range(mapping, start >> PAGE_CACHE_SHIFT,
+ end >> PAGE_CACHE_SHIFT);
}
-EXPORT_SYMBOL(sync_page_range_nolock);
+EXPORT_SYMBOL(filemap_fdatawait_range);
/**
* filemap_fdatawait - wait for all under-writeback pages to complete
@@ -2167,20 +2122,7 @@ generic_file_direct_write(struct kiocb *iocb, const struct iovec *iov,
}
*ppos = end;
}
-
- /*
- * Sync the fs metadata but not the minor inode changes and
- * of course not the data as we did direct DMA for the IO.
- * i_mutex is held, which protects generic_osync_inode() from
- * livelocking. AIO O_DIRECT ops attempt to sync metadata here.
- */
out:
- if ((written >= 0 || written == -EIOCBQUEUED) &&
- ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
- int err = generic_osync_inode(inode, mapping, OSYNC_METADATA);
- if (err < 0)
- written = err;
- }
return written;
}
EXPORT_SYMBOL(generic_file_direct_write);
@@ -2312,8 +2254,6 @@ generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
{
struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_mapping;
- const struct address_space_operations *a_ops = mapping->a_ops;
- struct inode *inode = mapping->host;
ssize_t status;
struct iov_iter i;
@@ -2323,16 +2263,6 @@ generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
if (likely(status >= 0)) {
written += status;
*ppos = pos + status;
-
- /*
- * For now, when the user asks for O_SYNC, we'll actually give
- * O_DSYNC
- */
- if (unlikely((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
- if (!a_ops->writepage || !is_sync_kiocb(iocb))
- status = generic_osync_inode(inode, mapping,
- OSYNC_METADATA|OSYNC_DATA);
- }
}
/*
@@ -2348,9 +2278,27 @@ generic_file_buffered_write(struct kiocb *iocb, const struct iovec *iov,
}
EXPORT_SYMBOL(generic_file_buffered_write);
-static ssize_t
-__generic_file_aio_write_nolock(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t *ppos)
+/**
+ * __generic_file_aio_write - write data to a file
+ * @iocb: IO state structure (file, offset, etc.)
+ * @iov: vector with data to write
+ * @nr_segs: number of segments in the vector
+ * @ppos: position where to write
+ *
+ * This function does all the work needed for actually writing data to a
+ * file. It does all basic checks, removes SUID from the file, updates
+ * modification times and calls proper subroutines depending on whether we
+ * do direct IO or a standard buffered write.
+ *
+ * It expects i_mutex to be grabbed unless we work on a block device or similar
+ * object which does not need locking at all.
+ *
+ * This function does *not* take care of syncing data in case of O_SYNC write.
+ * A caller has to handle it. This is mainly due to the fact that we want to
+ * avoid syncing under i_mutex.
+ */
+ssize_t __generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t *ppos)
{
struct file *file = iocb->ki_filp;
struct address_space * mapping = file->f_mapping;
@@ -2447,51 +2395,37 @@ out:
current->backing_dev_info = NULL;
return written ? written : err;
}
+EXPORT_SYMBOL(__generic_file_aio_write);
-ssize_t generic_file_aio_write_nolock(struct kiocb *iocb,
- const struct iovec *iov, unsigned long nr_segs, loff_t pos)
-{
- struct file *file = iocb->ki_filp;
- struct address_space *mapping = file->f_mapping;
- struct inode *inode = mapping->host;
- ssize_t ret;
-
- BUG_ON(iocb->ki_pos != pos);
-
- ret = __generic_file_aio_write_nolock(iocb, iov, nr_segs,
- &iocb->ki_pos);
-
- if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
- ssize_t err;
-
- err = sync_page_range_nolock(inode, mapping, pos, ret);
- if (err < 0)
- ret = err;
- }
- return ret;
-}
-EXPORT_SYMBOL(generic_file_aio_write_nolock);
-
+/**
+ * generic_file_aio_write - write data to a file
+ * @iocb: IO state structure
+ * @iov: vector with data to write
+ * @nr_segs: number of segments in the vector
+ * @pos: position in file where to write
+ *
+ * This is a wrapper around __generic_file_aio_write() to be used by most
+ * filesystems. It takes care of syncing the file in case of O_SYNC file
+ * and acquires i_mutex as needed.
+ */
ssize_t generic_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
struct file *file = iocb->ki_filp;
- struct address_space *mapping = file->f_mapping;
- struct inode *inode = mapping->host;
+ struct inode *inode = file->f_mapping->host;
ssize_t ret;
BUG_ON(iocb->ki_pos != pos);
mutex_lock(&inode->i_mutex);
- ret = __generic_file_aio_write_nolock(iocb, iov, nr_segs,
- &iocb->ki_pos);
+ ret = __generic_file_aio_write(iocb, iov, nr_segs, &iocb->ki_pos);
mutex_unlock(&inode->i_mutex);
- if (ret > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
+ if (ret > 0 || ret == -EIOCBQUEUED) {
ssize_t err;
- err = sync_page_range(inode, mapping, pos, ret);
- if (err < 0)
+ err = generic_write_sync(file, pos, ret);
+ if (err < 0 && ret > 0)
ret = err;
}
return ret;
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index cafdcee154e..b16d6363477 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -234,6 +234,7 @@ unsigned long vma_kernel_pagesize(struct vm_area_struct *vma)
return 1UL << (hstate->order + PAGE_SHIFT);
}
+EXPORT_SYMBOL_GPL(vma_kernel_pagesize);
/*
* Return the page size being used by the MMU to back a VMA. In the majority
diff --git a/mm/slob.c b/mm/slob.c
index 9641da3d5e5..837ebd64cc3 100644
--- a/mm/slob.c
+++ b/mm/slob.c
@@ -692,3 +692,8 @@ void __init kmem_cache_init(void)
{
slob_ready = 1;
}
+
+void __init kmem_cache_init_late(void)
+{
+ /* Nothing to do */
+}
diff --git a/mm/slub.c b/mm/slub.c
index b6276753626..417ed843b25 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -141,6 +141,13 @@
SLAB_POISON | SLAB_STORE_USER)
/*
+ * Debugging flags that require metadata to be stored in the slab. These get
+ * disabled when slub_debug=O is used and a cache's min order increases with
+ * metadata.
+ */
+#define DEBUG_METADATA_FLAGS (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER)
+
+/*
* Set of flags that will prevent slab merging
*/
#define SLUB_NEVER_MERGE (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \
@@ -325,6 +332,7 @@ static int slub_debug;
#endif
static char *slub_debug_slabs;
+static int disable_higher_order_debug;
/*
* Object debugging
@@ -646,7 +654,7 @@ static int slab_pad_check(struct kmem_cache *s, struct page *page)
slab_err(s, page, "Padding overwritten. 0x%p-0x%p", fault, end - 1);
print_section("Padding", end - remainder, remainder);
- restore_bytes(s, "slab padding", POISON_INUSE, start, end);
+ restore_bytes(s, "slab padding", POISON_INUSE, end - remainder, end);
return 0;
}
@@ -976,6 +984,15 @@ static int __init setup_slub_debug(char *str)
*/
goto check_slabs;
+ if (tolower(*str) == 'o') {
+ /*
+ * Avoid enabling debugging on caches if its minimum order
+ * would increase as a result.
+ */
+ disable_higher_order_debug = 1;
+ goto out;
+ }
+
slub_debug = 0;
if (*str == '-')
/*
@@ -1026,8 +1043,8 @@ static unsigned long kmem_cache_flags(unsigned long objsize,
* Enable debugging if selected on the kernel commandline.
*/
if (slub_debug && (!slub_debug_slabs ||
- strncmp(slub_debug_slabs, name, strlen(slub_debug_slabs)) == 0))
- flags |= slub_debug;
+ !strncmp(slub_debug_slabs, name, strlen(slub_debug_slabs))))
+ flags |= slub_debug;
return flags;
}
@@ -1109,8 +1126,7 @@ static struct page *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
}
if (kmemcheck_enabled
- && !(s->flags & (SLAB_NOTRACK | DEBUG_DEFAULT_FLAGS)))
- {
+ && !(s->flags & (SLAB_NOTRACK | DEBUG_DEFAULT_FLAGS))) {
int pages = 1 << oo_order(oo);
kmemcheck_alloc_shadow(page, oo_order(oo), flags, node);
@@ -1560,6 +1576,10 @@ slab_out_of_memory(struct kmem_cache *s, gfp_t gfpflags, int nid)
"default order: %d, min order: %d\n", s->name, s->objsize,
s->size, oo_order(s->oo), oo_order(s->min));
+ if (oo_order(s->min) > get_order(s->objsize))
+ printk(KERN_WARNING " %s debugging increased min order, use "
+ "slub_debug=O to disable.\n", s->name);
+
for_each_online_node(node) {
struct kmem_cache_node *n = get_node(s, node);
unsigned long nr_slabs;
@@ -2001,7 +2021,7 @@ static inline int calculate_order(int size)
return order;
fraction /= 2;
}
- min_objects --;
+ min_objects--;
}
/*
@@ -2400,6 +2420,7 @@ static int calculate_sizes(struct kmem_cache *s, int forced_order)
* on bootup.
*/
align = calculate_alignment(flags, align, s->objsize);
+ s->align = align;
/*
* SLUB stores one object immediately after another beginning from
@@ -2452,6 +2473,18 @@ static int kmem_cache_open(struct kmem_cache *s, gfp_t gfpflags,
if (!calculate_sizes(s, -1))
goto error;
+ if (disable_higher_order_debug) {
+ /*
+ * Disable debugging flags that store metadata if the min slab
+ * order increased.
+ */
+ if (get_order(s->size) > get_order(s->objsize)) {
+ s->flags &= ~DEBUG_METADATA_FLAGS;
+ s->offset = 0;
+ if (!calculate_sizes(s, -1))
+ goto error;
+ }
+ }
/*
* The larger the object size is, the more pages we want on the partial
@@ -2790,6 +2823,11 @@ static s8 size_index[24] = {
2 /* 192 */
};
+static inline int size_index_elem(size_t bytes)
+{
+ return (bytes - 1) / 8;
+}
+
static struct kmem_cache *get_slab(size_t size, gfp_t flags)
{
int index;
@@ -2798,7 +2836,7 @@ static struct kmem_cache *get_slab(size_t size, gfp_t flags)
if (!size)
return ZERO_SIZE_PTR;
- index = size_index[(size - 1) / 8];
+ index = size_index[size_index_elem(size)];
} else
index = fls(size - 1);
@@ -3156,10 +3194,12 @@ void __init kmem_cache_init(void)
slab_state = PARTIAL;
/* Caches that are not of the two-to-the-power-of size */
- if (KMALLOC_MIN_SIZE <= 64) {
+ if (KMALLOC_MIN_SIZE <= 32) {
create_kmalloc_cache(&kmalloc_caches[1],
"kmalloc-96", 96, GFP_NOWAIT);
caches++;
+ }
+ if (KMALLOC_MIN_SIZE <= 64) {
create_kmalloc_cache(&kmalloc_caches[2],
"kmalloc-192", 192, GFP_NOWAIT);
caches++;
@@ -3186,17 +3226,28 @@ void __init kmem_cache_init(void)
BUILD_BUG_ON(KMALLOC_MIN_SIZE > 256 ||
(KMALLOC_MIN_SIZE & (KMALLOC_MIN_SIZE - 1)));
- for (i = 8; i < KMALLOC_MIN_SIZE; i += 8)
- size_index[(i - 1) / 8] = KMALLOC_SHIFT_LOW;
+ for (i = 8; i < KMALLOC_MIN_SIZE; i += 8) {
+ int elem = size_index_elem(i);
+ if (elem >= ARRAY_SIZE(size_index))
+ break;
+ size_index[elem] = KMALLOC_SHIFT_LOW;
+ }
- if (KMALLOC_MIN_SIZE == 128) {
+ if (KMALLOC_MIN_SIZE == 64) {
+ /*
+ * The 96 byte size cache is not used if the alignment
+ * is 64 byte.
+ */
+ for (i = 64 + 8; i <= 96; i += 8)
+ size_index[size_index_elem(i)] = 7;
+ } else if (KMALLOC_MIN_SIZE == 128) {
/*
* The 192 byte sized cache is not used if the alignment
* is 128 byte. Redirect kmalloc to use the 256 byte cache
* instead.
*/
for (i = 128 + 8; i <= 192; i += 8)
- size_index[(i - 1) / 8] = 8;
+ size_index[size_index_elem(i)] = 8;
}
slab_state = UP;
@@ -4543,8 +4594,11 @@ static int sysfs_slab_add(struct kmem_cache *s)
}
err = sysfs_create_group(&s->kobj, &slab_attr_group);
- if (err)
+ if (err) {
+ kobject_del(&s->kobj);
+ kobject_put(&s->kobj);
return err;
+ }
kobject_uevent(&s->kobj, KOBJ_ADD);
if (!unmergeable) {
/* Setup first alias */
@@ -4726,7 +4780,7 @@ static const struct file_operations proc_slabinfo_operations = {
static int __init slab_proc_init(void)
{
- proc_create("slabinfo",S_IWUSR|S_IRUGO,NULL,&proc_slabinfo_operations);
+ proc_create("slabinfo", S_IRUGO, NULL, &proc_slabinfo_operations);
return 0;
}
module_init(slab_proc_init);
diff --git a/mm/swapfile.c b/mm/swapfile.c
index 8ffdc0d23c5..74f1102e874 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -161,7 +161,8 @@ static int discard_swap(struct swap_info_struct *si)
}
err = blkdev_issue_discard(si->bdev, start_block,
- nr_blocks, GFP_KERNEL);
+ nr_blocks, GFP_KERNEL,
+ DISCARD_FL_BARRIER);
if (err)
break;
@@ -200,7 +201,8 @@ static void discard_swap_cluster(struct swap_info_struct *si,
start_block <<= PAGE_SHIFT - 9;
nr_blocks <<= PAGE_SHIFT - 9;
if (blkdev_issue_discard(si->bdev, start_block,
- nr_blocks, GFP_NOIO))
+ nr_blocks, GFP_NOIO,
+ DISCARD_FL_BARRIER))
break;
}
diff --git a/scripts/extract-ikconfig b/scripts/extract-ikconfig
index 72997c353cb..de233ff43c1 100755
--- a/scripts/extract-ikconfig
+++ b/scripts/extract-ikconfig
@@ -17,6 +17,10 @@ dump_config() {
return
fi
end=`$binoffset $file $IKCFG_ED 2>/dev/null`
+ [ "$?" != "0" ] && end="-1"
+ if [ "$end" -eq "-1" ]; then
+ return
+ fi
start=`expr $start + 8`
size=`expr $end - $start`
@@ -55,6 +59,8 @@ dump_config "$image"
GZHDR1="0x1f 0x8b 0x08 0x00"
GZHDR2="0x1f 0x8b 0x08 0x08"
+ELFHDR="0x7f 0x45 0x4c 0x46"
+
# vmlinux.gz: Check for a compressed images
off=`$binoffset "$image" $GZHDR1 2>/dev/null`
[ "$?" != "0" ] && off="-1"
@@ -69,6 +75,14 @@ elif [ "$off" -ne "-1" ]; then
(dd ibs="$off" skip=1 count=0 && dd bs=512k) <"$image" 2>/dev/null | \
zcat >"$TMPFILE"
dump_config "$TMPFILE"
+
+# check if this is simply an ELF file
+else
+ off=`$binoffset "$image" $ELFHDR 2>/dev/null`
+ [ "$?" != "0" ] && off="-1"
+ if [ "$off" -eq "0" ]; then
+ dump_config "$image"
+ fi
fi
echo "ERROR: Unable to extract kernel configuration information."
diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index 5ddf8becd7a..6d69c7ccdcc 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -2,7 +2,8 @@
# Kernel configuration targets
# These targets are used from top-level makefile
-PHONY += oldconfig xconfig gconfig menuconfig config silentoldconfig update-po-config
+PHONY += oldconfig xconfig gconfig menuconfig config silentoldconfig update-po-config \
+ localmodconfig localyesconfig
ifdef KBUILD_KCONFIG
Kconfig := $(KBUILD_KCONFIG)
@@ -28,6 +29,35 @@ oldconfig: $(obj)/conf
silentoldconfig: $(obj)/conf
$< -s $(Kconfig)
+localmodconfig: $(obj)/streamline_config.pl $(obj)/conf
+ $(Q)perl $< $(Kconfig) > .tmp.config
+ $(Q)if [ -f .config ]; then \
+ cmp -s .tmp.config .config || \
+ (mv -f .config .config.old.1; \
+ mv -f .tmp.config .config; \
+ $(obj)/conf -s $(Kconfig); \
+ mv -f .config.old.1 .config.old) \
+ else \
+ mv -f .tmp.config .config; \
+ $(obj)/conf -s $(Kconfig); \
+ fi
+ $(Q)rm -f .tmp.config
+
+localyesconfig: $(obj)/streamline_config.pl $(obj)/conf
+ $(Q)perl $< $(Kconfig) > .tmp.config
+ $(Q)sed -i s/=m/=y/ .tmp.config
+ $(Q)if [ -f .config ]; then \
+ cmp -s .tmp.config .config || \
+ (mv -f .config .config.old.1; \
+ mv -f .tmp.config .config; \
+ $(obj)/conf -s $(Kconfig); \
+ mv -f .config.old.1 .config.old) \
+ else \
+ mv -f .tmp.config .config; \
+ $(obj)/conf -s $(Kconfig); \
+ fi
+ $(Q)rm -f .tmp.config
+
# Create new linux.pot file
# Adjust charset to UTF-8 in .po file to accept UTF-8 in Kconfig files
# The symlink is used to repair a deficiency in arch/um
@@ -83,6 +113,8 @@ help:
@echo ' xconfig - Update current config utilising a QT based front-end'
@echo ' gconfig - Update current config utilising a GTK based front-end'
@echo ' oldconfig - Update current config utilising a provided .config as base'
+ @echo ' localmodconfig - Update current config disabling modules not loaded'
+ @echo ' localyesconfig - Update current config converting local mods to core'
@echo ' silentoldconfig - Same as oldconfig, but quietly, additionally update deps'
@echo ' randconfig - New config with random answer to all options'
@echo ' defconfig - New config with default answer to all options'
diff --git a/scripts/kconfig/streamline_config.pl b/scripts/kconfig/streamline_config.pl
new file mode 100644
index 00000000000..95984db8e1e
--- /dev/null
+++ b/scripts/kconfig/streamline_config.pl
@@ -0,0 +1,366 @@
+#!/usr/bin/perl -w
+#
+# Copywrite 2005-2009 - Steven Rostedt
+# Licensed under the terms of the GNU GPL License version 2
+#
+# It's simple enough to figure out how this works.
+# If not, then you can ask me at stripconfig@goodmis.org
+#
+# What it does?
+#
+# If you have installed a Linux kernel from a distribution
+# that turns on way too many modules than you need, and
+# you only want the modules you use, then this program
+# is perfect for you.
+#
+# It gives you the ability to turn off all the modules that are
+# not loaded on your system.
+#
+# Howto:
+#
+# 1. Boot up the kernel that you want to stream line the config on.
+# 2. Change directory to the directory holding the source of the
+# kernel that you just booted.
+# 3. Copy the configuraton file to this directory as .config
+# 4. Have all your devices that you need modules for connected and
+# operational (make sure that their corresponding modules are loaded)
+# 5. Run this script redirecting the output to some other file
+# like config_strip.
+# 6. Back up your old config (if you want too).
+# 7. copy the config_strip file to .config
+# 8. Run "make oldconfig"
+#
+# Now your kernel is ready to be built with only the modules that
+# are loaded.
+#
+# Here's what I did with my Debian distribution.
+#
+# cd /usr/src/linux-2.6.10
+# cp /boot/config-2.6.10-1-686-smp .config
+# ~/bin/streamline_config > config_strip
+# mv .config config_sav
+# mv config_strip .config
+# make oldconfig
+#
+my $config = ".config";
+my $linuxpath = ".";
+
+my $uname = `uname -r`;
+chomp $uname;
+
+my @searchconfigs = (
+ {
+ "file" => ".config",
+ "exec" => "cat",
+ },
+ {
+ "file" => "/proc/config.gz",
+ "exec" => "zcat",
+ },
+ {
+ "file" => "/boot/config-$uname",
+ "exec" => "cat",
+ },
+ {
+ "file" => "/boot/vmlinuz-$uname",
+ "exec" => "scripts/extract-ikconfig",
+ "test" => "scripts/extract-ikconfig",
+ },
+ {
+ "file" => "vmlinux",
+ "exec" => "scripts/extract-ikconfig",
+ "test" => "scripts/extract-ikconfig",
+ },
+ {
+ "file" => "/lib/modules/$uname/kernel/kernel/configs.ko",
+ "exec" => "scripts/extract-ikconfig",
+ "test" => "scripts/extract-ikconfig",
+ },
+ {
+ "file" => "kernel/configs.ko",
+ "exec" => "scripts/extract-ikconfig",
+ "test" => "scripts/extract-ikconfig",
+ },
+ {
+ "file" => "kernel/configs.o",
+ "exec" => "scripts/extract-ikconfig",
+ "test" => "scripts/extract-ikconfig",
+ },
+);
+
+sub find_config {
+ foreach my $conf (@searchconfigs) {
+ my $file = $conf->{"file"};
+
+ next if ( ! -f "$file");
+
+ if (defined($conf->{"test"})) {
+ `$conf->{"test"} $conf->{"file"} 2>/dev/null`;
+ next if ($?);
+ }
+
+ my $exec = $conf->{"exec"};
+
+ print STDERR "using config: '$file'\n";
+
+ open(CIN, "$exec $file |") || die "Failed to run $exec $file";
+ return;
+ }
+ die "No config file found";
+}
+
+find_config;
+
+my @makefiles = `find $linuxpath -name Makefile`;
+my %depends;
+my %selects;
+my %prompts;
+my %objects;
+my $var;
+my $cont = 0;
+
+# Get the top level Kconfig file (passed in)
+my $kconfig = $ARGV[0];
+
+# prevent recursion
+my %read_kconfigs;
+
+sub read_kconfig {
+ my ($kconfig) = @_;
+
+ my $state = "NONE";
+ my $config;
+ my @kconfigs;
+
+ open(KIN, $kconfig) || die "Can't open $kconfig";
+ while (<KIN>) {
+ chomp;
+
+ # collect any Kconfig sources
+ if (/^source\s*"(.*)"/) {
+ $kconfigs[$#kconfigs+1] = $1;
+ }
+
+ # configs found
+ if (/^\s*config\s+(\S+)\s*$/) {
+ $state = "NEW";
+ $config = $1;
+
+ # collect the depends for the config
+ } elsif ($state eq "NEW" && /^\s*depends\s+on\s+(.*)$/) {
+ $state = "DEP";
+ $depends{$config} = $1;
+ } elsif ($state eq "DEP" && /^\s*depends\s+on\s+(.*)$/) {
+ $depends{$config} .= " " . $1;
+
+ # Get the configs that select this config
+ } elsif ($state ne "NONE" && /^\s*select\s+(\S+)/) {
+ if (defined($selects{$1})) {
+ $selects{$1} .= " " . $config;
+ } else {
+ $selects{$1} = $config;
+ }
+
+ # configs without prompts must be selected
+ } elsif ($state ne "NONE" && /^\s*tristate\s\S/) {
+ # note if the config has a prompt
+ $prompt{$config} = 1;
+
+ # stop on "help"
+ } elsif (/^\s*help\s*$/) {
+ $state = "NONE";
+ }
+ }
+ close(KIN);
+
+ # read in any configs that were found.
+ foreach $kconfig (@kconfigs) {
+ if (!defined($read_kconfigs{$kconfig})) {
+ $read_kconfigs{$kconfig} = 1;
+ read_kconfig($kconfig);
+ }
+ }
+}
+
+if ($kconfig) {
+ read_kconfig($kconfig);
+}
+
+# Read all Makefiles to map the configs to the objects
+foreach my $makefile (@makefiles) {
+ chomp $makefile;
+
+ open(MIN,$makefile) || die "Can't open $makefile";
+ while (<MIN>) {
+ my $objs;
+
+ # is this a line after a line with a backslash?
+ if ($cont && /(\S.*)$/) {
+ $objs = $1;
+ }
+ $cont = 0;
+
+ # collect objects after obj-$(CONFIG_FOO_BAR)
+ if (/obj-\$\((CONFIG_[^\)]*)\)\s*[+:]?=\s*(.*)/) {
+ $var = $1;
+ $objs = $2;
+ }
+ if (defined($objs)) {
+ # test if the line ends with a backslash
+ if ($objs =~ m,(.*)\\$,) {
+ $objs = $1;
+ $cont = 1;
+ }
+
+ foreach my $obj (split /\s+/,$objs) {
+ $obj =~ s/-/_/g;
+ if ($obj =~ /(.*)\.o$/) {
+ # Objects may bes enabled by more than one config.
+ # Store configs in an array.
+ my @arr;
+
+ if (defined($objects{$1})) {
+ @arr = @{$objects{$1}};
+ }
+
+ $arr[$#arr+1] = $var;
+
+ # The objects have a hash mapping to a reference
+ # of an array of configs.
+ $objects{$1} = \@arr;
+ }
+ }
+ }
+ }
+ close(MIN);
+}
+
+my %modules;
+
+# see what modules are loaded on this system
+open(LIN,"/sbin/lsmod|") || die "Cant lsmod";
+while (<LIN>) {
+ next if (/^Module/); # Skip the first line.
+ if (/^(\S+)/) {
+ $modules{$1} = 1;
+ }
+}
+close (LIN);
+
+# add to the configs hash all configs that are needed to enable
+# a loaded module.
+my %configs;
+foreach my $module (keys(%modules)) {
+ if (defined($objects{$module})) {
+ @arr = @{$objects{$module}};
+ foreach my $conf (@arr) {
+ $configs{$conf} = $module;
+ }
+ } else {
+ # Most likely, someone has a custom (binary?) module loaded.
+ print STDERR "$module config not found!!\n";
+ }
+}
+
+my $valid = "A-Za-z_0-9";
+my $repeat = 1;
+
+#
+# Note, we do not care about operands (like: &&, ||, !) we want to add any
+# config that is in the depend list of another config. This script does
+# not enable configs that are not already enabled. If we come across a
+# config A that depends on !B, we can still add B to the list of depends
+# to keep on. If A was on in the original config, B would not have been
+# and B would not be turned on by this script.
+#
+sub parse_config_dep_select
+{
+ my ($p) = @_;
+
+ while ($p =~ /[$valid]/) {
+
+ if ($p =~ /^[^$valid]*([$valid]+)/) {
+ my $conf = "CONFIG_" . $1;
+
+ $p =~ s/^[^$valid]*[$valid]+//;
+
+ if (!defined($configs{$conf})) {
+ # We must make sure that this config has its
+ # dependencies met.
+ $repeat = 1; # do again
+ $configs{$conf} = 1;
+ }
+ } else {
+ die "this should never happen";
+ }
+ }
+}
+
+while ($repeat) {
+ $repeat = 0;
+
+ foreach my $config (keys %configs) {
+ $config =~ s/^CONFIG_//;
+
+ if (defined($depends{$config})) {
+ # This config has dependencies. Make sure they are also included
+ parse_config_dep_select $depends{$config};
+ }
+
+ if (defined($prompt{$config}) || !defined($selects{$config})) {
+ next;
+ }
+
+ # config has no prompt and must be selected.
+ parse_config_dep_select $selects{$config};
+ }
+}
+
+my %setconfigs;
+
+# Finally, read the .config file and turn off any module enabled that
+# we could not find a reason to keep enabled.
+while(<CIN>) {
+
+ if (/CONFIG_IKCONFIG/) {
+ if (/# CONFIG_IKCONFIG is not set/) {
+ # enable IKCONFIG at least as a module
+ print "CONFIG_IKCONFIG=m\n";
+ # don't ask about PROC
+ print "# CONFIG_IKCONFIG_PROC is not set\n";
+ } else {
+ print;
+ }
+ next;
+ }
+
+ if (/^(CONFIG.*)=(m|y)/) {
+ if (defined($configs{$1})) {
+ $setconfigs{$1} = $2;
+ } elsif ($2 eq "m") {
+ print "# $1 is not set\n";
+ next;
+ }
+ }
+ print;
+}
+close(CIN);
+
+# Integrity check, make sure all modules that we want enabled do
+# indeed have their configs set.
+loop:
+foreach my $module (keys(%modules)) {
+ if (defined($objects{$module})) {
+ my @arr = @{$objects{$module}};
+ foreach my $conf (@arr) {
+ if (defined($setconfigs{$conf})) {
+ next loop;
+ }
+ }
+ print STDERR "module $module did not have configs";
+ foreach my $conf (@arr) {
+ print STDERR " " , $conf;
+ }
+ print STDERR "\n";
+ }
+}
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index 130b12118d4..885ba012557 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -193,38 +193,6 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = {
.set_bias_level = at91sam9g20ek_set_bias_level,
};
-/*
- * FIXME: This is a temporary bodge to avoid cross-tree merge issues.
- * New drivers should register the wm8731 I2C device in the machine
- * setup code (under arch/arm for ARM systems).
- */
-static int wm8731_i2c_register(void)
-{
- struct i2c_board_info info;
- struct i2c_adapter *adapter;
- struct i2c_client *client;
-
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = 0x1b;
- strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
-
- adapter = i2c_get_adapter(0);
- if (!adapter) {
- printk(KERN_ERR "can't get i2c adapter 0\n");
- return -ENODEV;
- }
-
- client = i2c_new_device(adapter, &info);
- i2c_put_adapter(adapter);
- if (!client) {
- printk(KERN_ERR "can't add i2c device at 0x%x\n",
- (unsigned int)info.addr);
- return -ENODEV;
- }
-
- return 0;
-}
-
static struct snd_soc_device at91sam9g20ek_snd_devdata = {
.card = &snd_soc_at91sam9g20ek,
.codec_dev = &soc_codec_dev_wm8731,
@@ -279,10 +247,6 @@ static int __init at91sam9g20ek_init(void)
}
ssc_p->ssc = ssc;
- ret = wm8731_i2c_register();
- if (ret != 0)
- goto err_ssc;
-
at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
if (!at91sam9g20ek_snd_device) {
printk(KERN_ERR "ASoC: Platform device allocation failed\n");
diff --git a/sound/soc/s3c24xx/s3c24xx-ac97.h b/sound/soc/s3c24xx/s3c24xx-ac97.h
index a96dcadf28b..e96f941a810 100644
--- a/sound/soc/s3c24xx/s3c24xx-ac97.h
+++ b/sound/soc/s3c24xx/s3c24xx-ac97.h
@@ -20,12 +20,6 @@
#define AC_CMD_ADDR(x) (x << 16)
#define AC_CMD_DATA(x) (x & 0xffff)
-#ifdef CONFIG_CPU_S3C2440
-#define IRQ_S3C244x_AC97 IRQ_S3C2440_AC97
-#else
-#define IRQ_S3C244x_AC97 IRQ_S3C2443_AC97
-#endif
-
extern struct snd_soc_dai s3c2443_ac97_dai[];
#endif /*S3C24XXAC97_H_*/
diff --git a/virt/kvm/Kconfig b/virt/kvm/Kconfig
new file mode 100644
index 00000000000..daece36c0a5
--- /dev/null
+++ b/virt/kvm/Kconfig
@@ -0,0 +1,14 @@
+# KVM common configuration items and defaults
+
+config HAVE_KVM
+ bool
+
+config HAVE_KVM_IRQCHIP
+ bool
+
+config HAVE_KVM_EVENTFD
+ bool
+ select EVENTFD
+
+config KVM_APIC_ARCHITECTURE
+ bool
diff --git a/virt/kvm/coalesced_mmio.c b/virt/kvm/coalesced_mmio.c
index 5ae620d32fa..04d69cd7049 100644
--- a/virt/kvm/coalesced_mmio.c
+++ b/virt/kvm/coalesced_mmio.c
@@ -14,32 +14,28 @@
#include "coalesced_mmio.h"
-static int coalesced_mmio_in_range(struct kvm_io_device *this,
- gpa_t addr, int len, int is_write)
+static inline struct kvm_coalesced_mmio_dev *to_mmio(struct kvm_io_device *dev)
+{
+ return container_of(dev, struct kvm_coalesced_mmio_dev, dev);
+}
+
+static int coalesced_mmio_in_range(struct kvm_coalesced_mmio_dev *dev,
+ gpa_t addr, int len)
{
- struct kvm_coalesced_mmio_dev *dev =
- (struct kvm_coalesced_mmio_dev*)this->private;
struct kvm_coalesced_mmio_zone *zone;
- int next;
+ struct kvm_coalesced_mmio_ring *ring;
+ unsigned avail;
int i;
- if (!is_write)
- return 0;
-
- /* kvm->lock is taken by the caller and must be not released before
- * dev.read/write
- */
-
/* Are we able to batch it ? */
/* last is the first free entry
* check if we don't meet the first used entry
* there is always one unused entry in the buffer
*/
-
- next = (dev->kvm->coalesced_mmio_ring->last + 1) %
- KVM_COALESCED_MMIO_MAX;
- if (next == dev->kvm->coalesced_mmio_ring->first) {
+ ring = dev->kvm->coalesced_mmio_ring;
+ avail = (ring->first - ring->last - 1) % KVM_COALESCED_MMIO_MAX;
+ if (avail < KVM_MAX_VCPUS) {
/* full */
return 0;
}
@@ -60,14 +56,15 @@ static int coalesced_mmio_in_range(struct kvm_io_device *this,
return 0;
}
-static void coalesced_mmio_write(struct kvm_io_device *this,
- gpa_t addr, int len, const void *val)
+static int coalesced_mmio_write(struct kvm_io_device *this,
+ gpa_t addr, int len, const void *val)
{
- struct kvm_coalesced_mmio_dev *dev =
- (struct kvm_coalesced_mmio_dev*)this->private;
+ struct kvm_coalesced_mmio_dev *dev = to_mmio(this);
struct kvm_coalesced_mmio_ring *ring = dev->kvm->coalesced_mmio_ring;
+ if (!coalesced_mmio_in_range(dev, addr, len))
+ return -EOPNOTSUPP;
- /* kvm->lock must be taken by caller before call to in_range()*/
+ spin_lock(&dev->lock);
/* copy data in first free entry of the ring */
@@ -76,29 +73,40 @@ static void coalesced_mmio_write(struct kvm_io_device *this,
memcpy(ring->coalesced_mmio[ring->last].data, val, len);
smp_wmb();
ring->last = (ring->last + 1) % KVM_COALESCED_MMIO_MAX;
+ spin_unlock(&dev->lock);
+ return 0;
}
static void coalesced_mmio_destructor(struct kvm_io_device *this)
{
- kfree(this);
+ struct kvm_coalesced_mmio_dev *dev = to_mmio(this);
+
+ kfree(dev);
}
+static const struct kvm_io_device_ops coalesced_mmio_ops = {
+ .write = coalesced_mmio_write,
+ .destructor = coalesced_mmio_destructor,
+};
+
int kvm_coalesced_mmio_init(struct kvm *kvm)
{
struct kvm_coalesced_mmio_dev *dev;
+ int ret;
dev = kzalloc(sizeof(struct kvm_coalesced_mmio_dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
- dev->dev.write = coalesced_mmio_write;
- dev->dev.in_range = coalesced_mmio_in_range;
- dev->dev.destructor = coalesced_mmio_destructor;
- dev->dev.private = dev;
+ spin_lock_init(&dev->lock);
+ kvm_iodevice_init(&dev->dev, &coalesced_mmio_ops);
dev->kvm = kvm;
kvm->coalesced_mmio_dev = dev;
- kvm_io_bus_register_dev(&kvm->mmio_bus, &dev->dev);
- return 0;
+ ret = kvm_io_bus_register_dev(kvm, &kvm->mmio_bus, &dev->dev);
+ if (ret < 0)
+ kfree(dev);
+
+ return ret;
}
int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm,
@@ -109,16 +117,16 @@ int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm,
if (dev == NULL)
return -EINVAL;
- mutex_lock(&kvm->lock);
+ down_write(&kvm->slots_lock);
if (dev->nb_zones >= KVM_COALESCED_MMIO_ZONE_MAX) {
- mutex_unlock(&kvm->lock);
+ up_write(&kvm->slots_lock);
return -ENOBUFS;
}
dev->zone[dev->nb_zones] = *zone;
dev->nb_zones++;
- mutex_unlock(&kvm->lock);
+ up_write(&kvm->slots_lock);
return 0;
}
@@ -132,7 +140,7 @@ int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
if (dev == NULL)
return -EINVAL;
- mutex_lock(&kvm->lock);
+ down_write(&kvm->slots_lock);
i = dev->nb_zones;
while(i) {
@@ -150,7 +158,7 @@ int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
i--;
}
- mutex_unlock(&kvm->lock);
+ up_write(&kvm->slots_lock);
return 0;
}
diff --git a/virt/kvm/coalesced_mmio.h b/virt/kvm/coalesced_mmio.h
index 5ac0ec62846..4b49f27fa31 100644
--- a/virt/kvm/coalesced_mmio.h
+++ b/virt/kvm/coalesced_mmio.h
@@ -12,6 +12,7 @@
struct kvm_coalesced_mmio_dev {
struct kvm_io_device dev;
struct kvm *kvm;
+ spinlock_t lock;
int nb_zones;
struct kvm_coalesced_mmio_zone zone[KVM_COALESCED_MMIO_ZONE_MAX];
};
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c
new file mode 100644
index 00000000000..bb4ebd89b9f
--- /dev/null
+++ b/virt/kvm/eventfd.c
@@ -0,0 +1,578 @@
+/*
+ * kvm eventfd support - use eventfd objects to signal various KVM events
+ *
+ * Copyright 2009 Novell. All Rights Reserved.
+ *
+ * Author:
+ * Gregory Haskins <ghaskins@novell.com>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * 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, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kvm_host.h>
+#include <linux/kvm.h>
+#include <linux/workqueue.h>
+#include <linux/syscalls.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/file.h>
+#include <linux/list.h>
+#include <linux/eventfd.h>
+#include <linux/kernel.h>
+
+#include "iodev.h"
+
+/*
+ * --------------------------------------------------------------------
+ * irqfd: Allows an fd to be used to inject an interrupt to the guest
+ *
+ * Credit goes to Avi Kivity for the original idea.
+ * --------------------------------------------------------------------
+ */
+
+struct _irqfd {
+ struct kvm *kvm;
+ struct eventfd_ctx *eventfd;
+ int gsi;
+ struct list_head list;
+ poll_table pt;
+ wait_queue_head_t *wqh;
+ wait_queue_t wait;
+ struct work_struct inject;
+ struct work_struct shutdown;
+};
+
+static struct workqueue_struct *irqfd_cleanup_wq;
+
+static void
+irqfd_inject(struct work_struct *work)
+{
+ struct _irqfd *irqfd = container_of(work, struct _irqfd, inject);
+ struct kvm *kvm = irqfd->kvm;
+
+ mutex_lock(&kvm->irq_lock);
+ kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, irqfd->gsi, 1);
+ kvm_set_irq(kvm, KVM_USERSPACE_IRQ_SOURCE_ID, irqfd->gsi, 0);
+ mutex_unlock(&kvm->irq_lock);
+}
+
+/*
+ * Race-free decouple logic (ordering is critical)
+ */
+static void
+irqfd_shutdown(struct work_struct *work)
+{
+ struct _irqfd *irqfd = container_of(work, struct _irqfd, shutdown);
+
+ /*
+ * Synchronize with the wait-queue and unhook ourselves to prevent
+ * further events.
+ */
+ remove_wait_queue(irqfd->wqh, &irqfd->wait);
+
+ /*
+ * We know no new events will be scheduled at this point, so block
+ * until all previously outstanding events have completed
+ */
+ flush_work(&irqfd->inject);
+
+ /*
+ * It is now safe to release the object's resources
+ */
+ eventfd_ctx_put(irqfd->eventfd);
+ kfree(irqfd);
+}
+
+
+/* assumes kvm->irqfds.lock is held */
+static bool
+irqfd_is_active(struct _irqfd *irqfd)
+{
+ return list_empty(&irqfd->list) ? false : true;
+}
+
+/*
+ * Mark the irqfd as inactive and schedule it for removal
+ *
+ * assumes kvm->irqfds.lock is held
+ */
+static void
+irqfd_deactivate(struct _irqfd *irqfd)
+{
+ BUG_ON(!irqfd_is_active(irqfd));
+
+ list_del_init(&irqfd->list);
+
+ queue_work(irqfd_cleanup_wq, &irqfd->shutdown);
+}
+
+/*
+ * Called with wqh->lock held and interrupts disabled
+ */
+static int
+irqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key)
+{
+ struct _irqfd *irqfd = container_of(wait, struct _irqfd, wait);
+ unsigned long flags = (unsigned long)key;
+
+ if (flags & POLLIN)
+ /* An event has been signaled, inject an interrupt */
+ schedule_work(&irqfd->inject);
+
+ if (flags & POLLHUP) {
+ /* The eventfd is closing, detach from KVM */
+ struct kvm *kvm = irqfd->kvm;
+ unsigned long flags;
+
+ spin_lock_irqsave(&kvm->irqfds.lock, flags);
+
+ /*
+ * We must check if someone deactivated the irqfd before
+ * we could acquire the irqfds.lock since the item is
+ * deactivated from the KVM side before it is unhooked from
+ * the wait-queue. If it is already deactivated, we can
+ * simply return knowing the other side will cleanup for us.
+ * We cannot race against the irqfd going away since the
+ * other side is required to acquire wqh->lock, which we hold
+ */
+ if (irqfd_is_active(irqfd))
+ irqfd_deactivate(irqfd);
+
+ spin_unlock_irqrestore(&kvm->irqfds.lock, flags);
+ }
+
+ return 0;
+}
+
+static void
+irqfd_ptable_queue_proc(struct file *file, wait_queue_head_t *wqh,
+ poll_table *pt)
+{
+ struct _irqfd *irqfd = container_of(pt, struct _irqfd, pt);
+
+ irqfd->wqh = wqh;
+ add_wait_queue(wqh, &irqfd->wait);
+}
+
+static int
+kvm_irqfd_assign(struct kvm *kvm, int fd, int gsi)
+{
+ struct _irqfd *irqfd;
+ struct file *file = NULL;
+ struct eventfd_ctx *eventfd = NULL;
+ int ret;
+ unsigned int events;
+
+ irqfd = kzalloc(sizeof(*irqfd), GFP_KERNEL);
+ if (!irqfd)
+ return -ENOMEM;
+
+ irqfd->kvm = kvm;
+ irqfd->gsi = gsi;
+ INIT_LIST_HEAD(&irqfd->list);
+ INIT_WORK(&irqfd->inject, irqfd_inject);
+ INIT_WORK(&irqfd->shutdown, irqfd_shutdown);
+
+ file = eventfd_fget(fd);
+ if (IS_ERR(file)) {
+ ret = PTR_ERR(file);
+ goto fail;
+ }
+
+ eventfd = eventfd_ctx_fileget(file);
+ if (IS_ERR(eventfd)) {
+ ret = PTR_ERR(eventfd);
+ goto fail;
+ }
+
+ irqfd->eventfd = eventfd;
+
+ /*
+ * Install our own custom wake-up handling so we are notified via
+ * a callback whenever someone signals the underlying eventfd
+ */
+ init_waitqueue_func_entry(&irqfd->wait, irqfd_wakeup);
+ init_poll_funcptr(&irqfd->pt, irqfd_ptable_queue_proc);
+
+ events = file->f_op->poll(file, &irqfd->pt);
+
+ spin_lock_irq(&kvm->irqfds.lock);
+ list_add_tail(&irqfd->list, &kvm->irqfds.items);
+ spin_unlock_irq(&kvm->irqfds.lock);
+
+ /*
+ * Check if there was an event already pending on the eventfd
+ * before we registered, and trigger it as if we didn't miss it.
+ */
+ if (events & POLLIN)
+ schedule_work(&irqfd->inject);
+
+ /*
+ * do not drop the file until the irqfd is fully initialized, otherwise
+ * we might race against the POLLHUP
+ */
+ fput(file);
+
+ return 0;
+
+fail:
+ if (eventfd && !IS_ERR(eventfd))
+ eventfd_ctx_put(eventfd);
+
+ if (!IS_ERR(file))
+ fput(file);
+
+ kfree(irqfd);
+ return ret;
+}
+
+void
+kvm_eventfd_init(struct kvm *kvm)
+{
+ spin_lock_init(&kvm->irqfds.lock);
+ INIT_LIST_HEAD(&kvm->irqfds.items);
+ INIT_LIST_HEAD(&kvm->ioeventfds);
+}
+
+/*
+ * shutdown any irqfd's that match fd+gsi
+ */
+static int
+kvm_irqfd_deassign(struct kvm *kvm, int fd, int gsi)
+{
+ struct _irqfd *irqfd, *tmp;
+ struct eventfd_ctx *eventfd;
+
+ eventfd = eventfd_ctx_fdget(fd);
+ if (IS_ERR(eventfd))
+ return PTR_ERR(eventfd);
+
+ spin_lock_irq(&kvm->irqfds.lock);
+
+ list_for_each_entry_safe(irqfd, tmp, &kvm->irqfds.items, list) {
+ if (irqfd->eventfd == eventfd && irqfd->gsi == gsi)
+ irqfd_deactivate(irqfd);
+ }
+
+ spin_unlock_irq(&kvm->irqfds.lock);
+ eventfd_ctx_put(eventfd);
+
+ /*
+ * Block until we know all outstanding shutdown jobs have completed
+ * so that we guarantee there will not be any more interrupts on this
+ * gsi once this deassign function returns.
+ */
+ flush_workqueue(irqfd_cleanup_wq);
+
+ return 0;
+}
+
+int
+kvm_irqfd(struct kvm *kvm, int fd, int gsi, int flags)
+{
+ if (flags & KVM_IRQFD_FLAG_DEASSIGN)
+ return kvm_irqfd_deassign(kvm, fd, gsi);
+
+ return kvm_irqfd_assign(kvm, fd, gsi);
+}
+
+/*
+ * This function is called as the kvm VM fd is being released. Shutdown all
+ * irqfds that still remain open
+ */
+void
+kvm_irqfd_release(struct kvm *kvm)
+{
+ struct _irqfd *irqfd, *tmp;
+
+ spin_lock_irq(&kvm->irqfds.lock);
+
+ list_for_each_entry_safe(irqfd, tmp, &kvm->irqfds.items, list)
+ irqfd_deactivate(irqfd);
+
+ spin_unlock_irq(&kvm->irqfds.lock);
+
+ /*
+ * Block until we know all outstanding shutdown jobs have completed
+ * since we do not take a kvm* reference.
+ */
+ flush_workqueue(irqfd_cleanup_wq);
+
+}
+
+/*
+ * create a host-wide workqueue for issuing deferred shutdown requests
+ * aggregated from all vm* instances. We need our own isolated single-thread
+ * queue to prevent deadlock against flushing the normal work-queue.
+ */
+static int __init irqfd_module_init(void)
+{
+ irqfd_cleanup_wq = create_singlethread_workqueue("kvm-irqfd-cleanup");
+ if (!irqfd_cleanup_wq)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void __exit irqfd_module_exit(void)
+{
+ destroy_workqueue(irqfd_cleanup_wq);
+}
+
+module_init(irqfd_module_init);
+module_exit(irqfd_module_exit);
+
+/*
+ * --------------------------------------------------------------------
+ * ioeventfd: translate a PIO/MMIO memory write to an eventfd signal.
+ *
+ * userspace can register a PIO/MMIO address with an eventfd for receiving
+ * notification when the memory has been touched.
+ * --------------------------------------------------------------------
+ */
+
+struct _ioeventfd {
+ struct list_head list;
+ u64 addr;
+ int length;
+ struct eventfd_ctx *eventfd;
+ u64 datamatch;
+ struct kvm_io_device dev;
+ bool wildcard;
+};
+
+static inline struct _ioeventfd *
+to_ioeventfd(struct kvm_io_device *dev)
+{
+ return container_of(dev, struct _ioeventfd, dev);
+}
+
+static void
+ioeventfd_release(struct _ioeventfd *p)
+{
+ eventfd_ctx_put(p->eventfd);
+ list_del(&p->list);
+ kfree(p);
+}
+
+static bool
+ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val)
+{
+ u64 _val;
+
+ if (!(addr == p->addr && len == p->length))
+ /* address-range must be precise for a hit */
+ return false;
+
+ if (p->wildcard)
+ /* all else equal, wildcard is always a hit */
+ return true;
+
+ /* otherwise, we have to actually compare the data */
+
+ BUG_ON(!IS_ALIGNED((unsigned long)val, len));
+
+ switch (len) {
+ case 1:
+ _val = *(u8 *)val;
+ break;
+ case 2:
+ _val = *(u16 *)val;
+ break;
+ case 4:
+ _val = *(u32 *)val;
+ break;
+ case 8:
+ _val = *(u64 *)val;
+ break;
+ default:
+ return false;
+ }
+
+ return _val == p->datamatch ? true : false;
+}
+
+/* MMIO/PIO writes trigger an event if the addr/val match */
+static int
+ioeventfd_write(struct kvm_io_device *this, gpa_t addr, int len,
+ const void *val)
+{
+ struct _ioeventfd *p = to_ioeventfd(this);
+
+ if (!ioeventfd_in_range(p, addr, len, val))
+ return -EOPNOTSUPP;
+
+ eventfd_signal(p->eventfd, 1);
+ return 0;
+}
+
+/*
+ * This function is called as KVM is completely shutting down. We do not
+ * need to worry about locking just nuke anything we have as quickly as possible
+ */
+static void
+ioeventfd_destructor(struct kvm_io_device *this)
+{
+ struct _ioeventfd *p = to_ioeventfd(this);
+
+ ioeventfd_release(p);
+}
+
+static const struct kvm_io_device_ops ioeventfd_ops = {
+ .write = ioeventfd_write,
+ .destructor = ioeventfd_destructor,
+};
+
+/* assumes kvm->slots_lock held */
+static bool
+ioeventfd_check_collision(struct kvm *kvm, struct _ioeventfd *p)
+{
+ struct _ioeventfd *_p;
+
+ list_for_each_entry(_p, &kvm->ioeventfds, list)
+ if (_p->addr == p->addr && _p->length == p->length &&
+ (_p->wildcard || p->wildcard ||
+ _p->datamatch == p->datamatch))
+ return true;
+
+ return false;
+}
+
+static int
+kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
+{
+ int pio = args->flags & KVM_IOEVENTFD_FLAG_PIO;
+ struct kvm_io_bus *bus = pio ? &kvm->pio_bus : &kvm->mmio_bus;
+ struct _ioeventfd *p;
+ struct eventfd_ctx *eventfd;
+ int ret;
+
+ /* must be natural-word sized */
+ switch (args->len) {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* check for range overflow */
+ if (args->addr + args->len < args->addr)
+ return -EINVAL;
+
+ /* check for extra flags that we don't understand */
+ if (args->flags & ~KVM_IOEVENTFD_VALID_FLAG_MASK)
+ return -EINVAL;
+
+ eventfd = eventfd_ctx_fdget(args->fd);
+ if (IS_ERR(eventfd))
+ return PTR_ERR(eventfd);
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ INIT_LIST_HEAD(&p->list);
+ p->addr = args->addr;
+ p->length = args->len;
+ p->eventfd = eventfd;
+
+ /* The datamatch feature is optional, otherwise this is a wildcard */
+ if (args->flags & KVM_IOEVENTFD_FLAG_DATAMATCH)
+ p->datamatch = args->datamatch;
+ else
+ p->wildcard = true;
+
+ down_write(&kvm->slots_lock);
+
+ /* Verify that there isnt a match already */
+ if (ioeventfd_check_collision(kvm, p)) {
+ ret = -EEXIST;
+ goto unlock_fail;
+ }
+
+ kvm_iodevice_init(&p->dev, &ioeventfd_ops);
+
+ ret = __kvm_io_bus_register_dev(bus, &p->dev);
+ if (ret < 0)
+ goto unlock_fail;
+
+ list_add_tail(&p->list, &kvm->ioeventfds);
+
+ up_write(&kvm->slots_lock);
+
+ return 0;
+
+unlock_fail:
+ up_write(&kvm->slots_lock);
+
+fail:
+ kfree(p);
+ eventfd_ctx_put(eventfd);
+
+ return ret;
+}
+
+static int
+kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
+{
+ int pio = args->flags & KVM_IOEVENTFD_FLAG_PIO;
+ struct kvm_io_bus *bus = pio ? &kvm->pio_bus : &kvm->mmio_bus;
+ struct _ioeventfd *p, *tmp;
+ struct eventfd_ctx *eventfd;
+ int ret = -ENOENT;
+
+ eventfd = eventfd_ctx_fdget(args->fd);
+ if (IS_ERR(eventfd))
+ return PTR_ERR(eventfd);
+
+ down_write(&kvm->slots_lock);
+
+ list_for_each_entry_safe(p, tmp, &kvm->ioeventfds, list) {
+ bool wildcard = !(args->flags & KVM_IOEVENTFD_FLAG_DATAMATCH);
+
+ if (p->eventfd != eventfd ||
+ p->addr != args->addr ||
+ p->length != args->len ||
+ p->wildcard != wildcard)
+ continue;
+
+ if (!p->wildcard && p->datamatch != args->datamatch)
+ continue;
+
+ __kvm_io_bus_unregister_dev(bus, &p->dev);
+ ioeventfd_release(p);
+ ret = 0;
+ break;
+ }
+
+ up_write(&kvm->slots_lock);
+
+ eventfd_ctx_put(eventfd);
+
+ return ret;
+}
+
+int
+kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)
+{
+ if (args->flags & KVM_IOEVENTFD_FLAG_DEASSIGN)
+ return kvm_deassign_ioeventfd(kvm, args);
+
+ return kvm_assign_ioeventfd(kvm, args);
+}
diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c
index 1150c6d5c7b..9fe140bb38e 100644
--- a/virt/kvm/ioapic.c
+++ b/virt/kvm/ioapic.c
@@ -36,6 +36,7 @@
#include <asm/processor.h>
#include <asm/page.h>
#include <asm/current.h>
+#include <trace/events/kvm.h>
#include "ioapic.h"
#include "lapic.h"
@@ -103,6 +104,7 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)
{
unsigned index;
bool mask_before, mask_after;
+ union kvm_ioapic_redirect_entry *e;
switch (ioapic->ioregsel) {
case IOAPIC_REG_VERSION:
@@ -122,19 +124,20 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)
ioapic_debug("change redir index %x val %x\n", index, val);
if (index >= IOAPIC_NUM_PINS)
return;
- mask_before = ioapic->redirtbl[index].fields.mask;
+ e = &ioapic->redirtbl[index];
+ mask_before = e->fields.mask;
if (ioapic->ioregsel & 1) {
- ioapic->redirtbl[index].bits &= 0xffffffff;
- ioapic->redirtbl[index].bits |= (u64) val << 32;
+ e->bits &= 0xffffffff;
+ e->bits |= (u64) val << 32;
} else {
- ioapic->redirtbl[index].bits &= ~0xffffffffULL;
- ioapic->redirtbl[index].bits |= (u32) val;
- ioapic->redirtbl[index].fields.remote_irr = 0;
+ e->bits &= ~0xffffffffULL;
+ e->bits |= (u32) val;
+ e->fields.remote_irr = 0;
}
- mask_after = ioapic->redirtbl[index].fields.mask;
+ mask_after = e->fields.mask;
if (mask_before != mask_after)
kvm_fire_mask_notifiers(ioapic->kvm, index, mask_after);
- if (ioapic->redirtbl[index].fields.trig_mode == IOAPIC_LEVEL_TRIG
+ if (e->fields.trig_mode == IOAPIC_LEVEL_TRIG
&& ioapic->irr & (1 << index))
ioapic_service(ioapic, index);
break;
@@ -164,7 +167,9 @@ static int ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
/* Always delivery PIT interrupt to vcpu 0 */
if (irq == 0) {
irqe.dest_mode = 0; /* Physical mode. */
- irqe.dest_id = ioapic->kvm->vcpus[0]->vcpu_id;
+ /* need to read apic_id from apic regiest since
+ * it can be rewritten */
+ irqe.dest_id = ioapic->kvm->bsp_vcpu->vcpu_id;
}
#endif
return kvm_irq_delivery_to_apic(ioapic->kvm, NULL, &irqe);
@@ -188,7 +193,10 @@ int kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level)
if ((edge && old_irr != ioapic->irr) ||
(!edge && !entry.fields.remote_irr))
ret = ioapic_service(ioapic, irq);
+ else
+ ret = 0; /* report coalesced interrupt */
}
+ trace_kvm_ioapic_set_irq(entry.bits, irq, ret == 0);
}
return ret;
}
@@ -220,24 +228,29 @@ void kvm_ioapic_update_eoi(struct kvm *kvm, int vector, int trigger_mode)
__kvm_ioapic_update_eoi(ioapic, i, trigger_mode);
}
-static int ioapic_in_range(struct kvm_io_device *this, gpa_t addr,
- int len, int is_write)
+static inline struct kvm_ioapic *to_ioapic(struct kvm_io_device *dev)
{
- struct kvm_ioapic *ioapic = (struct kvm_ioapic *)this->private;
+ return container_of(dev, struct kvm_ioapic, dev);
+}
+static inline int ioapic_in_range(struct kvm_ioapic *ioapic, gpa_t addr)
+{
return ((addr >= ioapic->base_address &&
(addr < ioapic->base_address + IOAPIC_MEM_LENGTH)));
}
-static void ioapic_mmio_read(struct kvm_io_device *this, gpa_t addr, int len,
- void *val)
+static int ioapic_mmio_read(struct kvm_io_device *this, gpa_t addr, int len,
+ void *val)
{
- struct kvm_ioapic *ioapic = (struct kvm_ioapic *)this->private;
+ struct kvm_ioapic *ioapic = to_ioapic(this);
u32 result;
+ if (!ioapic_in_range(ioapic, addr))
+ return -EOPNOTSUPP;
ioapic_debug("addr %lx\n", (unsigned long)addr);
ASSERT(!(addr & 0xf)); /* check alignment */
+ mutex_lock(&ioapic->kvm->irq_lock);
addr &= 0xff;
switch (addr) {
case IOAPIC_REG_SELECT:
@@ -264,22 +277,28 @@ static void ioapic_mmio_read(struct kvm_io_device *this, gpa_t addr, int len,
default:
printk(KERN_WARNING "ioapic: wrong length %d\n", len);
}
+ mutex_unlock(&ioapic->kvm->irq_lock);
+ return 0;
}
-static void ioapic_mmio_write(struct kvm_io_device *this, gpa_t addr, int len,
- const void *val)
+static int ioapic_mmio_write(struct kvm_io_device *this, gpa_t addr, int len,
+ const void *val)
{
- struct kvm_ioapic *ioapic = (struct kvm_ioapic *)this->private;
+ struct kvm_ioapic *ioapic = to_ioapic(this);
u32 data;
+ if (!ioapic_in_range(ioapic, addr))
+ return -EOPNOTSUPP;
ioapic_debug("ioapic_mmio_write addr=%p len=%d val=%p\n",
(void*)addr, len, val);
ASSERT(!(addr & 0xf)); /* check alignment */
+
+ mutex_lock(&ioapic->kvm->irq_lock);
if (len == 4 || len == 8)
data = *(u32 *) val;
else {
printk(KERN_WARNING "ioapic: Unsupported size %d\n", len);
- return;
+ goto unlock;
}
addr &= 0xff;
@@ -300,6 +319,9 @@ static void ioapic_mmio_write(struct kvm_io_device *this, gpa_t addr, int len,
default:
break;
}
+unlock:
+ mutex_unlock(&ioapic->kvm->irq_lock);
+ return 0;
}
void kvm_ioapic_reset(struct kvm_ioapic *ioapic)
@@ -314,21 +336,27 @@ void kvm_ioapic_reset(struct kvm_ioapic *ioapic)
ioapic->id = 0;
}
+static const struct kvm_io_device_ops ioapic_mmio_ops = {
+ .read = ioapic_mmio_read,
+ .write = ioapic_mmio_write,
+};
+
int kvm_ioapic_init(struct kvm *kvm)
{
struct kvm_ioapic *ioapic;
+ int ret;
ioapic = kzalloc(sizeof(struct kvm_ioapic), GFP_KERNEL);
if (!ioapic)
return -ENOMEM;
kvm->arch.vioapic = ioapic;
kvm_ioapic_reset(ioapic);
- ioapic->dev.read = ioapic_mmio_read;
- ioapic->dev.write = ioapic_mmio_write;
- ioapic->dev.in_range = ioapic_in_range;
- ioapic->dev.private = ioapic;
+ kvm_iodevice_init(&ioapic->dev, &ioapic_mmio_ops);
ioapic->kvm = kvm;
- kvm_io_bus_register_dev(&kvm->mmio_bus, &ioapic->dev);
- return 0;
+ ret = kvm_io_bus_register_dev(kvm, &kvm->mmio_bus, &ioapic->dev);
+ if (ret < 0)
+ kfree(ioapic);
+
+ return ret;
}
diff --git a/virt/kvm/iodev.h b/virt/kvm/iodev.h
index 55e8846ac3a..12fd3caffd2 100644
--- a/virt/kvm/iodev.h
+++ b/virt/kvm/iodev.h
@@ -17,49 +17,54 @@
#define __KVM_IODEV_H__
#include <linux/kvm_types.h>
+#include <asm/errno.h>
-struct kvm_io_device {
- void (*read)(struct kvm_io_device *this,
+struct kvm_io_device;
+
+/**
+ * kvm_io_device_ops are called under kvm slots_lock.
+ * read and write handlers return 0 if the transaction has been handled,
+ * or non-zero to have it passed to the next device.
+ **/
+struct kvm_io_device_ops {
+ int (*read)(struct kvm_io_device *this,
+ gpa_t addr,
+ int len,
+ void *val);
+ int (*write)(struct kvm_io_device *this,
gpa_t addr,
int len,
- void *val);
- void (*write)(struct kvm_io_device *this,
- gpa_t addr,
- int len,
- const void *val);
- int (*in_range)(struct kvm_io_device *this, gpa_t addr, int len,
- int is_write);
+ const void *val);
void (*destructor)(struct kvm_io_device *this);
+};
- void *private;
+
+struct kvm_io_device {
+ const struct kvm_io_device_ops *ops;
};
-static inline void kvm_iodevice_read(struct kvm_io_device *dev,
- gpa_t addr,
- int len,
- void *val)
+static inline void kvm_iodevice_init(struct kvm_io_device *dev,
+ const struct kvm_io_device_ops *ops)
{
- dev->read(dev, addr, len, val);
+ dev->ops = ops;
}
-static inline void kvm_iodevice_write(struct kvm_io_device *dev,
- gpa_t addr,
- int len,
- const void *val)
+static inline int kvm_iodevice_read(struct kvm_io_device *dev,
+ gpa_t addr, int l, void *v)
{
- dev->write(dev, addr, len, val);
+ return dev->ops->read ? dev->ops->read(dev, addr, l, v) : -EOPNOTSUPP;
}
-static inline int kvm_iodevice_inrange(struct kvm_io_device *dev,
- gpa_t addr, int len, int is_write)
+static inline int kvm_iodevice_write(struct kvm_io_device *dev,
+ gpa_t addr, int l, const void *v)
{
- return dev->in_range(dev, addr, len, is_write);
+ return dev->ops->write ? dev->ops->write(dev, addr, l, v) : -EOPNOTSUPP;
}
static inline void kvm_iodevice_destructor(struct kvm_io_device *dev)
{
- if (dev->destructor)
- dev->destructor(dev);
+ if (dev->ops->destructor)
+ dev->ops->destructor(dev);
}
#endif /* __KVM_IODEV_H__ */
diff --git a/virt/kvm/irq_comm.c b/virt/kvm/irq_comm.c
index ddc17f0e2f3..001663ff401 100644
--- a/virt/kvm/irq_comm.c
+++ b/virt/kvm/irq_comm.c
@@ -20,6 +20,7 @@
*/
#include <linux/kvm_host.h>
+#include <trace/events/kvm.h>
#include <asm/msidef.h>
#ifdef CONFIG_IA64
@@ -62,14 +63,14 @@ int kvm_irq_delivery_to_apic(struct kvm *kvm, struct kvm_lapic *src,
int i, r = -1;
struct kvm_vcpu *vcpu, *lowest = NULL;
+ WARN_ON(!mutex_is_locked(&kvm->irq_lock));
+
if (irq->dest_mode == 0 && irq->dest_id == 0xff &&
kvm_is_dm_lowest_prio(irq))
printk(KERN_INFO "kvm: apic: phys broadcast and lowest prio\n");
- for (i = 0; i < KVM_MAX_VCPUS; i++) {
- vcpu = kvm->vcpus[i];
-
- if (!vcpu || !kvm_apic_present(vcpu))
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ if (!kvm_apic_present(vcpu))
continue;
if (!kvm_apic_match_dest(vcpu, src, irq->shorthand,
@@ -99,6 +100,8 @@ static int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
{
struct kvm_lapic_irq irq;
+ trace_kvm_msi_set_irq(e->msi.address_lo, e->msi.data);
+
irq.dest_id = (e->msi.address_lo &
MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
irq.vector = (e->msi.data &
@@ -113,7 +116,7 @@ static int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e,
return kvm_irq_delivery_to_apic(kvm, NULL, &irq);
}
-/* This should be called with the kvm->lock mutex held
+/* This should be called with the kvm->irq_lock mutex held
* Return value:
* < 0 Interrupt was ignored (masked or not delivered for other reasons)
* = 0 Interrupt was coalesced (previous irq is still pending)
@@ -125,6 +128,10 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, int irq, int level)
unsigned long *irq_state, sig_level;
int ret = -1;
+ trace_kvm_set_irq(irq, level, irq_source_id);
+
+ WARN_ON(!mutex_is_locked(&kvm->irq_lock));
+
if (irq < KVM_IOAPIC_NUM_PINS) {
irq_state = (unsigned long *)&kvm->arch.irq_states[irq];
@@ -134,7 +141,9 @@ int kvm_set_irq(struct kvm *kvm, int irq_source_id, int irq, int level)
else
clear_bit(irq_source_id, irq_state);
sig_level = !!(*irq_state);
- } else /* Deal with MSI/MSI-X */
+ } else if (!level)
+ return ret;
+ else /* Deal with MSI/MSI-X */
sig_level = 1;
/* Not possible to detect if the guest uses the PIC or the
@@ -159,6 +168,8 @@ void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin)
struct hlist_node *n;
unsigned gsi = pin;
+ trace_kvm_ack_irq(irqchip, pin);
+
list_for_each_entry(e, &kvm->irq_routing, link)
if (e->type == KVM_IRQ_ROUTING_IRQCHIP &&
e->irqchip.irqchip == irqchip &&
@@ -175,19 +186,26 @@ void kvm_notify_acked_irq(struct kvm *kvm, unsigned irqchip, unsigned pin)
void kvm_register_irq_ack_notifier(struct kvm *kvm,
struct kvm_irq_ack_notifier *kian)
{
+ mutex_lock(&kvm->irq_lock);
hlist_add_head(&kian->link, &kvm->arch.irq_ack_notifier_list);
+ mutex_unlock(&kvm->irq_lock);
}
-void kvm_unregister_irq_ack_notifier(struct kvm_irq_ack_notifier *kian)
+void kvm_unregister_irq_ack_notifier(struct kvm *kvm,
+ struct kvm_irq_ack_notifier *kian)
{
+ mutex_lock(&kvm->irq_lock);
hlist_del_init(&kian->link);
+ mutex_unlock(&kvm->irq_lock);
}
-/* The caller must hold kvm->lock mutex */
int kvm_request_irq_source_id(struct kvm *kvm)
{
unsigned long *bitmap = &kvm->arch.irq_sources_bitmap;
- int irq_source_id = find_first_zero_bit(bitmap,
+ int irq_source_id;
+
+ mutex_lock(&kvm->irq_lock);
+ irq_source_id = find_first_zero_bit(bitmap,
sizeof(kvm->arch.irq_sources_bitmap));
if (irq_source_id >= sizeof(kvm->arch.irq_sources_bitmap)) {
@@ -197,6 +215,7 @@ int kvm_request_irq_source_id(struct kvm *kvm)
ASSERT(irq_source_id != KVM_USERSPACE_IRQ_SOURCE_ID);
set_bit(irq_source_id, bitmap);
+ mutex_unlock(&kvm->irq_lock);
return irq_source_id;
}
@@ -207,6 +226,7 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id)
ASSERT(irq_source_id != KVM_USERSPACE_IRQ_SOURCE_ID);
+ mutex_lock(&kvm->irq_lock);
if (irq_source_id < 0 ||
irq_source_id >= sizeof(kvm->arch.irq_sources_bitmap)) {
printk(KERN_ERR "kvm: IRQ source ID out of range!\n");
@@ -215,19 +235,24 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id)
for (i = 0; i < KVM_IOAPIC_NUM_PINS; i++)
clear_bit(irq_source_id, &kvm->arch.irq_states[i]);
clear_bit(irq_source_id, &kvm->arch.irq_sources_bitmap);
+ mutex_unlock(&kvm->irq_lock);
}
void kvm_register_irq_mask_notifier(struct kvm *kvm, int irq,
struct kvm_irq_mask_notifier *kimn)
{
+ mutex_lock(&kvm->irq_lock);
kimn->irq = irq;
hlist_add_head(&kimn->link, &kvm->mask_notifier_list);
+ mutex_unlock(&kvm->irq_lock);
}
void kvm_unregister_irq_mask_notifier(struct kvm *kvm, int irq,
struct kvm_irq_mask_notifier *kimn)
{
+ mutex_lock(&kvm->irq_lock);
hlist_del(&kimn->link);
+ mutex_unlock(&kvm->irq_lock);
}
void kvm_fire_mask_notifiers(struct kvm *kvm, int irq, bool mask)
@@ -235,6 +260,8 @@ void kvm_fire_mask_notifiers(struct kvm *kvm, int irq, bool mask)
struct kvm_irq_mask_notifier *kimn;
struct hlist_node *n;
+ WARN_ON(!mutex_is_locked(&kvm->irq_lock));
+
hlist_for_each_entry(kimn, n, &kvm->mask_notifier_list, link)
if (kimn->irq == irq)
kimn->func(kimn, mask);
@@ -250,7 +277,9 @@ static void __kvm_free_irq_routing(struct list_head *irq_routing)
void kvm_free_irq_routing(struct kvm *kvm)
{
+ mutex_lock(&kvm->irq_lock);
__kvm_free_irq_routing(&kvm->irq_routing);
+ mutex_unlock(&kvm->irq_lock);
}
static int setup_routing_entry(struct kvm_kernel_irq_routing_entry *e,
@@ -325,13 +354,13 @@ int kvm_set_irq_routing(struct kvm *kvm,
e = NULL;
}
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->irq_lock);
list_splice(&kvm->irq_routing, &tmp);
INIT_LIST_HEAD(&kvm->irq_routing);
list_splice(&irq_list, &kvm->irq_routing);
INIT_LIST_HEAD(&irq_list);
list_splice(&tmp, &irq_list);
- mutex_unlock(&kvm->lock);
+ mutex_unlock(&kvm->irq_lock);
r = 0;
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 2884baf1d5f..897bff3b7df 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -59,9 +59,18 @@
#include "irq.h"
#endif
+#define CREATE_TRACE_POINTS
+#include <trace/events/kvm.h>
+
MODULE_AUTHOR("Qumranet");
MODULE_LICENSE("GPL");
+/*
+ * Ordering of locks:
+ *
+ * kvm->slots_lock --> kvm->lock --> kvm->irq_lock
+ */
+
DEFINE_SPINLOCK(kvm_lock);
LIST_HEAD(vm_list);
@@ -79,6 +88,8 @@ static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl,
static bool kvm_rebooting;
+static bool largepages_enabled = true;
+
#ifdef KVM_CAP_DEVICE_ASSIGNMENT
static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev(struct list_head *head,
int assigned_dev_id)
@@ -120,17 +131,13 @@ static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work)
{
struct kvm_assigned_dev_kernel *assigned_dev;
struct kvm *kvm;
- int irq, i;
+ int i;
assigned_dev = container_of(work, struct kvm_assigned_dev_kernel,
interrupt_work);
kvm = assigned_dev->kvm;
- /* This is taken to safely inject irq inside the guest. When
- * the interrupt injection (or the ioapic code) uses a
- * finer-grained lock, update this
- */
- mutex_lock(&kvm->lock);
+ mutex_lock(&kvm->irq_lock);
spin_lock_irq(&assigned_dev->assigned_dev_lock);
if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_HOST_MSIX) {
struct kvm_guest_msix_entry *guest_entries =
@@ -143,23 +150,13 @@ static void kvm_assigned_dev_interrupt_work_handler(struct work_struct *work)
kvm_set_irq(assigned_dev->kvm,
assigned_dev->irq_source_id,
guest_entries[i].vector, 1);
- irq = assigned_dev->host_msix_entries[i].vector;
- if (irq != 0)
- enable_irq(irq);
- assigned_dev->host_irq_disabled = false;
}
- } else {
+ } else
kvm_set_irq(assigned_dev->kvm, assigned_dev->irq_source_id,
assigned_dev->guest_irq, 1);
- if (assigned_dev->irq_requested_type &
- KVM_DEV_IRQ_GUEST_MSI) {
- enable_irq(assigned_dev->host_irq);
- assigned_dev->host_irq_disabled = false;
- }
- }
spin_unlock_irq(&assigned_dev->assigned_dev_lock);
- mutex_unlock(&assigned_dev->kvm->lock);
+ mutex_unlock(&assigned_dev->kvm->irq_lock);
}
static irqreturn_t kvm_assigned_dev_intr(int irq, void *dev_id)
@@ -179,8 +176,10 @@ static irqreturn_t kvm_assigned_dev_intr(int irq, void *dev_id)
schedule_work(&assigned_dev->interrupt_work);
- disable_irq_nosync(irq);
- assigned_dev->host_irq_disabled = true;
+ if (assigned_dev->irq_requested_type & KVM_DEV_IRQ_GUEST_INTX) {
+ disable_irq_nosync(irq);
+ assigned_dev->host_irq_disabled = true;
+ }
out:
spin_unlock_irqrestore(&assigned_dev->assigned_dev_lock, flags);
@@ -215,7 +214,7 @@ static void kvm_assigned_dev_ack_irq(struct kvm_irq_ack_notifier *kian)
static void deassign_guest_irq(struct kvm *kvm,
struct kvm_assigned_dev_kernel *assigned_dev)
{
- kvm_unregister_irq_ack_notifier(&assigned_dev->ack_notifier);
+ kvm_unregister_irq_ack_notifier(kvm, &assigned_dev->ack_notifier);
assigned_dev->ack_notifier.gsi = -1;
if (assigned_dev->irq_source_id != -1)
@@ -417,6 +416,7 @@ static int assigned_device_enable_guest_msi(struct kvm *kvm,
{
dev->guest_irq = irq->guest_irq;
dev->ack_notifier.gsi = -1;
+ dev->host_irq_disabled = false;
return 0;
}
#endif
@@ -427,6 +427,7 @@ static int assigned_device_enable_guest_msix(struct kvm *kvm,
{
dev->guest_irq = irq->guest_irq;
dev->ack_notifier.gsi = -1;
+ dev->host_irq_disabled = false;
return 0;
}
#endif
@@ -693,11 +694,6 @@ out:
}
#endif
-static inline int valid_vcpu(int n)
-{
- return likely(n >= 0 && n < KVM_MAX_VCPUS);
-}
-
inline int kvm_is_mmio_pfn(pfn_t pfn)
{
if (pfn_valid(pfn)) {
@@ -745,12 +741,9 @@ static bool make_all_cpus_request(struct kvm *kvm, unsigned int req)
if (alloc_cpumask_var(&cpus, GFP_ATOMIC))
cpumask_clear(cpus);
- me = get_cpu();
spin_lock(&kvm->requests_lock);
- for (i = 0; i < KVM_MAX_VCPUS; ++i) {
- vcpu = kvm->vcpus[i];
- if (!vcpu)
- continue;
+ me = smp_processor_id();
+ kvm_for_each_vcpu(i, vcpu, kvm) {
if (test_and_set_bit(req, &vcpu->requests))
continue;
cpu = vcpu->cpu;
@@ -764,7 +757,6 @@ static bool make_all_cpus_request(struct kvm *kvm, unsigned int req)
else
called = false;
spin_unlock(&kvm->requests_lock);
- put_cpu();
free_cpumask_var(cpus);
return called;
}
@@ -986,7 +978,9 @@ static struct kvm *kvm_create_vm(void)
spin_lock_init(&kvm->mmu_lock);
spin_lock_init(&kvm->requests_lock);
kvm_io_bus_init(&kvm->pio_bus);
+ kvm_eventfd_init(kvm);
mutex_init(&kvm->lock);
+ mutex_init(&kvm->irq_lock);
kvm_io_bus_init(&kvm->mmio_bus);
init_rwsem(&kvm->slots_lock);
atomic_set(&kvm->users_count, 1);
@@ -1006,19 +1000,25 @@ out:
static void kvm_free_physmem_slot(struct kvm_memory_slot *free,
struct kvm_memory_slot *dont)
{
+ int i;
+
if (!dont || free->rmap != dont->rmap)
vfree(free->rmap);
if (!dont || free->dirty_bitmap != dont->dirty_bitmap)
vfree(free->dirty_bitmap);
- if (!dont || free->lpage_info != dont->lpage_info)
- vfree(free->lpage_info);
+
+ for (i = 0; i < KVM_NR_PAGE_SIZES - 1; ++i) {
+ if (!dont || free->lpage_info[i] != dont->lpage_info[i]) {
+ vfree(free->lpage_info[i]);
+ free->lpage_info[i] = NULL;
+ }
+ }
free->npages = 0;
free->dirty_bitmap = NULL;
free->rmap = NULL;
- free->lpage_info = NULL;
}
void kvm_free_physmem(struct kvm *kvm)
@@ -1071,6 +1071,8 @@ static int kvm_vm_release(struct inode *inode, struct file *filp)
{
struct kvm *kvm = filp->private_data;
+ kvm_irqfd_release(kvm);
+
kvm_put_kvm(kvm);
return 0;
}
@@ -1089,8 +1091,8 @@ int __kvm_set_memory_region(struct kvm *kvm,
{
int r;
gfn_t base_gfn;
- unsigned long npages, ugfn;
- unsigned long largepages, i;
+ unsigned long npages;
+ unsigned long i;
struct kvm_memory_slot *memslot;
struct kvm_memory_slot old, new;
@@ -1164,31 +1166,51 @@ int __kvm_set_memory_region(struct kvm *kvm,
else
new.userspace_addr = 0;
}
- if (npages && !new.lpage_info) {
- largepages = 1 + (base_gfn + npages - 1) / KVM_PAGES_PER_HPAGE;
- largepages -= base_gfn / KVM_PAGES_PER_HPAGE;
+ if (!npages)
+ goto skip_lpage;
- new.lpage_info = vmalloc(largepages * sizeof(*new.lpage_info));
+ for (i = 0; i < KVM_NR_PAGE_SIZES - 1; ++i) {
+ unsigned long ugfn;
+ unsigned long j;
+ int lpages;
+ int level = i + 2;
- if (!new.lpage_info)
+ /* Avoid unused variable warning if no large pages */
+ (void)level;
+
+ if (new.lpage_info[i])
+ continue;
+
+ lpages = 1 + (base_gfn + npages - 1) /
+ KVM_PAGES_PER_HPAGE(level);
+ lpages -= base_gfn / KVM_PAGES_PER_HPAGE(level);
+
+ new.lpage_info[i] = vmalloc(lpages * sizeof(*new.lpage_info[i]));
+
+ if (!new.lpage_info[i])
goto out_free;
- memset(new.lpage_info, 0, largepages * sizeof(*new.lpage_info));
+ memset(new.lpage_info[i], 0,
+ lpages * sizeof(*new.lpage_info[i]));
- if (base_gfn % KVM_PAGES_PER_HPAGE)
- new.lpage_info[0].write_count = 1;
- if ((base_gfn+npages) % KVM_PAGES_PER_HPAGE)
- new.lpage_info[largepages-1].write_count = 1;
+ if (base_gfn % KVM_PAGES_PER_HPAGE(level))
+ new.lpage_info[i][0].write_count = 1;
+ if ((base_gfn+npages) % KVM_PAGES_PER_HPAGE(level))
+ new.lpage_info[i][lpages - 1].write_count = 1;
ugfn = new.userspace_addr >> PAGE_SHIFT;
/*
* If the gfn and userspace address are not aligned wrt each
- * other, disable large page support for this slot
+ * other, or if explicitly asked to, disable large page
+ * support for this slot
*/
- if ((base_gfn ^ ugfn) & (KVM_PAGES_PER_HPAGE - 1))
- for (i = 0; i < largepages; ++i)
- new.lpage_info[i].write_count = 1;
+ if ((base_gfn ^ ugfn) & (KVM_PAGES_PER_HPAGE(level) - 1) ||
+ !largepages_enabled)
+ for (j = 0; j < lpages; ++j)
+ new.lpage_info[i][j].write_count = 1;
}
+skip_lpage:
+
/* Allocate page dirty bitmap if needed */
if ((new.flags & KVM_MEM_LOG_DIRTY_PAGES) && !new.dirty_bitmap) {
unsigned dirty_bytes = ALIGN(npages, BITS_PER_LONG) / 8;
@@ -1200,6 +1222,10 @@ int __kvm_set_memory_region(struct kvm *kvm,
if (old.npages)
kvm_arch_flush_shadow(kvm);
}
+#else /* not defined CONFIG_S390 */
+ new.user_alloc = user_alloc;
+ if (user_alloc)
+ new.userspace_addr = mem->userspace_addr;
#endif /* not defined CONFIG_S390 */
if (!npages)
@@ -1299,6 +1325,12 @@ out:
return r;
}
+void kvm_disable_largepages(void)
+{
+ largepages_enabled = false;
+}
+EXPORT_SYMBOL_GPL(kvm_disable_largepages);
+
int is_error_page(struct page *page)
{
return page == bad_page;
@@ -1635,9 +1667,7 @@ void kvm_vcpu_block(struct kvm_vcpu *vcpu)
for (;;) {
prepare_to_wait(&vcpu->wq, &wait, TASK_INTERRUPTIBLE);
- if ((kvm_arch_interrupt_allowed(vcpu) &&
- kvm_cpu_has_interrupt(vcpu)) ||
- kvm_arch_vcpu_runnable(vcpu)) {
+ if (kvm_arch_vcpu_runnable(vcpu)) {
set_bit(KVM_REQ_UNHALT, &vcpu->requests);
break;
}
@@ -1714,24 +1744,18 @@ static struct file_operations kvm_vcpu_fops = {
*/
static int create_vcpu_fd(struct kvm_vcpu *vcpu)
{
- int fd = anon_inode_getfd("kvm-vcpu", &kvm_vcpu_fops, vcpu, 0);
- if (fd < 0)
- kvm_put_kvm(vcpu->kvm);
- return fd;
+ return anon_inode_getfd("kvm-vcpu", &kvm_vcpu_fops, vcpu, 0);
}
/*
* Creates some virtual cpus. Good luck creating more than one.
*/
-static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, int n)
+static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id)
{
int r;
- struct kvm_vcpu *vcpu;
-
- if (!valid_vcpu(n))
- return -EINVAL;
+ struct kvm_vcpu *vcpu, *v;
- vcpu = kvm_arch_vcpu_create(kvm, n);
+ vcpu = kvm_arch_vcpu_create(kvm, id);
if (IS_ERR(vcpu))
return PTR_ERR(vcpu);
@@ -1742,23 +1766,38 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, int n)
return r;
mutex_lock(&kvm->lock);
- if (kvm->vcpus[n]) {
- r = -EEXIST;
+ if (atomic_read(&kvm->online_vcpus) == KVM_MAX_VCPUS) {
+ r = -EINVAL;
goto vcpu_destroy;
}
- kvm->vcpus[n] = vcpu;
- mutex_unlock(&kvm->lock);
+
+ kvm_for_each_vcpu(r, v, kvm)
+ if (v->vcpu_id == id) {
+ r = -EEXIST;
+ goto vcpu_destroy;
+ }
+
+ BUG_ON(kvm->vcpus[atomic_read(&kvm->online_vcpus)]);
/* Now it's all set up, let userspace reach it */
kvm_get_kvm(kvm);
r = create_vcpu_fd(vcpu);
- if (r < 0)
- goto unlink;
+ if (r < 0) {
+ kvm_put_kvm(kvm);
+ goto vcpu_destroy;
+ }
+
+ kvm->vcpus[atomic_read(&kvm->online_vcpus)] = vcpu;
+ smp_wmb();
+ atomic_inc(&kvm->online_vcpus);
+
+#ifdef CONFIG_KVM_APIC_ARCHITECTURE
+ if (kvm->bsp_vcpu_id == id)
+ kvm->bsp_vcpu = vcpu;
+#endif
+ mutex_unlock(&kvm->lock);
return r;
-unlink:
- mutex_lock(&kvm->lock);
- kvm->vcpus[n] = NULL;
vcpu_destroy:
mutex_unlock(&kvm->lock);
kvm_arch_vcpu_destroy(vcpu);
@@ -2199,6 +2238,7 @@ static long kvm_vm_ioctl(struct file *filp,
vfree(entries);
break;
}
+#endif /* KVM_CAP_IRQ_ROUTING */
#ifdef __KVM_HAVE_MSIX
case KVM_ASSIGN_SET_MSIX_NR: {
struct kvm_assigned_msix_nr entry_nr;
@@ -2221,7 +2261,35 @@ static long kvm_vm_ioctl(struct file *filp,
break;
}
#endif
-#endif /* KVM_CAP_IRQ_ROUTING */
+ case KVM_IRQFD: {
+ struct kvm_irqfd data;
+
+ r = -EFAULT;
+ if (copy_from_user(&data, argp, sizeof data))
+ goto out;
+ r = kvm_irqfd(kvm, data.fd, data.gsi, data.flags);
+ break;
+ }
+ case KVM_IOEVENTFD: {
+ struct kvm_ioeventfd data;
+
+ r = -EFAULT;
+ if (copy_from_user(&data, argp, sizeof data))
+ goto out;
+ r = kvm_ioeventfd(kvm, &data);
+ break;
+ }
+#ifdef CONFIG_KVM_APIC_ARCHITECTURE
+ case KVM_SET_BOOT_CPU_ID:
+ r = 0;
+ mutex_lock(&kvm->lock);
+ if (atomic_read(&kvm->online_vcpus) != 0)
+ r = -EBUSY;
+ else
+ kvm->bsp_vcpu_id = arg;
+ mutex_unlock(&kvm->lock);
+ break;
+#endif
default:
r = kvm_arch_vm_ioctl(filp, ioctl, arg);
}
@@ -2288,6 +2356,9 @@ static long kvm_dev_ioctl_check_extension_generic(long arg)
case KVM_CAP_USER_MEMORY:
case KVM_CAP_DESTROY_MEMORY_REGION_WORKS:
case KVM_CAP_JOIN_MEMORY_REGIONS_WORKS:
+#ifdef CONFIG_KVM_APIC_ARCHITECTURE
+ case KVM_CAP_SET_BOOT_CPU_ID:
+#endif
return 1;
#ifdef CONFIG_HAVE_KVM_IRQCHIP
case KVM_CAP_IRQ_ROUTING:
@@ -2335,7 +2406,7 @@ static long kvm_dev_ioctl(struct file *filp,
case KVM_TRACE_ENABLE:
case KVM_TRACE_PAUSE:
case KVM_TRACE_DISABLE:
- r = kvm_trace_ioctl(ioctl, arg);
+ r = -EOPNOTSUPP;
break;
default:
return kvm_arch_dev_ioctl(filp, ioctl, arg);
@@ -2449,26 +2520,71 @@ void kvm_io_bus_destroy(struct kvm_io_bus *bus)
}
}
-struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus,
- gpa_t addr, int len, int is_write)
+/* kvm_io_bus_write - called under kvm->slots_lock */
+int kvm_io_bus_write(struct kvm_io_bus *bus, gpa_t addr,
+ int len, const void *val)
{
int i;
+ for (i = 0; i < bus->dev_count; i++)
+ if (!kvm_iodevice_write(bus->devs[i], addr, len, val))
+ return 0;
+ return -EOPNOTSUPP;
+}
- for (i = 0; i < bus->dev_count; i++) {
- struct kvm_io_device *pos = bus->devs[i];
+/* kvm_io_bus_read - called under kvm->slots_lock */
+int kvm_io_bus_read(struct kvm_io_bus *bus, gpa_t addr, int len, void *val)
+{
+ int i;
+ for (i = 0; i < bus->dev_count; i++)
+ if (!kvm_iodevice_read(bus->devs[i], addr, len, val))
+ return 0;
+ return -EOPNOTSUPP;
+}
- if (pos->in_range(pos, addr, len, is_write))
- return pos;
- }
+int kvm_io_bus_register_dev(struct kvm *kvm, struct kvm_io_bus *bus,
+ struct kvm_io_device *dev)
+{
+ int ret;
- return NULL;
+ down_write(&kvm->slots_lock);
+ ret = __kvm_io_bus_register_dev(bus, dev);
+ up_write(&kvm->slots_lock);
+
+ return ret;
}
-void kvm_io_bus_register_dev(struct kvm_io_bus *bus, struct kvm_io_device *dev)
+/* An unlocked version. Caller must have write lock on slots_lock. */
+int __kvm_io_bus_register_dev(struct kvm_io_bus *bus,
+ struct kvm_io_device *dev)
{
- BUG_ON(bus->dev_count > (NR_IOBUS_DEVS-1));
+ if (bus->dev_count > NR_IOBUS_DEVS-1)
+ return -ENOSPC;
bus->devs[bus->dev_count++] = dev;
+
+ return 0;
+}
+
+void kvm_io_bus_unregister_dev(struct kvm *kvm,
+ struct kvm_io_bus *bus,
+ struct kvm_io_device *dev)
+{
+ down_write(&kvm->slots_lock);
+ __kvm_io_bus_unregister_dev(bus, dev);
+ up_write(&kvm->slots_lock);
+}
+
+/* An unlocked version. Caller must have write lock on slots_lock. */
+void __kvm_io_bus_unregister_dev(struct kvm_io_bus *bus,
+ struct kvm_io_device *dev)
+{
+ int i;
+
+ for (i = 0; i < bus->dev_count; i++)
+ if (bus->devs[i] == dev) {
+ bus->devs[i] = bus->devs[--bus->dev_count];
+ break;
+ }
}
static struct notifier_block kvm_cpu_notifier = {
@@ -2501,11 +2617,9 @@ static int vcpu_stat_get(void *_offset, u64 *val)
*val = 0;
spin_lock(&kvm_lock);
list_for_each_entry(kvm, &vm_list, vm_list)
- for (i = 0; i < KVM_MAX_VCPUS; ++i) {
- vcpu = kvm->vcpus[i];
- if (vcpu)
- *val += *(u32 *)((void *)vcpu + offset);
- }
+ kvm_for_each_vcpu(i, vcpu, kvm)
+ *val += *(u32 *)((void *)vcpu + offset);
+
spin_unlock(&kvm_lock);
return 0;
}
@@ -2679,15 +2793,15 @@ out_free_0:
__free_page(bad_page);
out:
kvm_arch_exit();
- kvm_exit_debug();
out_fail:
+ kvm_exit_debug();
return r;
}
EXPORT_SYMBOL_GPL(kvm_init);
void kvm_exit(void)
{
- kvm_trace_cleanup();
+ tracepoint_synchronize_unregister();
misc_deregister(&kvm_dev);
kmem_cache_destroy(kvm_vcpu_cache);
sysdev_unregister(&kvm_sysdev);
diff --git a/virt/kvm/kvm_trace.c b/virt/kvm/kvm_trace.c
deleted file mode 100644
index f5987444644..00000000000
--- a/virt/kvm/kvm_trace.c
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * kvm trace
- *
- * It is designed to allow debugging traces of kvm to be generated
- * on UP / SMP machines. Each trace entry can be timestamped so that
- * it's possible to reconstruct a chronological record of trace events.
- * The implementation refers to blktrace kernel support.
- *
- * Copyright (c) 2008 Intel Corporation
- * Copyright (C) 2006 Jens Axboe <axboe@kernel.dk>
- *
- * Authors: Feng(Eric) Liu, eric.e.liu@intel.com
- *
- * Date: Feb 2008
- */
-
-#include <linux/module.h>
-#include <linux/relay.h>
-#include <linux/debugfs.h>
-#include <linux/ktime.h>
-
-#include <linux/kvm_host.h>
-
-#define KVM_TRACE_STATE_RUNNING (1 << 0)
-#define KVM_TRACE_STATE_PAUSE (1 << 1)
-#define KVM_TRACE_STATE_CLEARUP (1 << 2)
-
-struct kvm_trace {
- int trace_state;
- struct rchan *rchan;
- struct dentry *lost_file;
- atomic_t lost_records;
-};
-static struct kvm_trace *kvm_trace;
-
-struct kvm_trace_probe {
- const char *name;
- const char *format;
- u32 timestamp_in;
- marker_probe_func *probe_func;
-};
-
-static inline int calc_rec_size(int timestamp, int extra)
-{
- int rec_size = KVM_TRC_HEAD_SIZE;
-
- rec_size += extra;
- return timestamp ? rec_size += KVM_TRC_CYCLE_SIZE : rec_size;
-}
-
-static void kvm_add_trace(void *probe_private, void *call_data,
- const char *format, va_list *args)
-{
- struct kvm_trace_probe *p = probe_private;
- struct kvm_trace *kt = kvm_trace;
- struct kvm_trace_rec rec;
- struct kvm_vcpu *vcpu;
- int i, size;
- u32 extra;
-
- if (unlikely(kt->trace_state != KVM_TRACE_STATE_RUNNING))
- return;
-
- rec.rec_val = TRACE_REC_EVENT_ID(va_arg(*args, u32));
- vcpu = va_arg(*args, struct kvm_vcpu *);
- rec.pid = current->tgid;
- rec.vcpu_id = vcpu->vcpu_id;
-
- extra = va_arg(*args, u32);
- WARN_ON(!(extra <= KVM_TRC_EXTRA_MAX));
- extra = min_t(u32, extra, KVM_TRC_EXTRA_MAX);
-
- rec.rec_val |= TRACE_REC_TCS(p->timestamp_in)
- | TRACE_REC_NUM_DATA_ARGS(extra);
-
- if (p->timestamp_in) {
- rec.u.timestamp.timestamp = ktime_to_ns(ktime_get());
-
- for (i = 0; i < extra; i++)
- rec.u.timestamp.extra_u32[i] = va_arg(*args, u32);
- } else {
- for (i = 0; i < extra; i++)
- rec.u.notimestamp.extra_u32[i] = va_arg(*args, u32);
- }
-
- size = calc_rec_size(p->timestamp_in, extra * sizeof(u32));
- relay_write(kt->rchan, &rec, size);
-}
-
-static struct kvm_trace_probe kvm_trace_probes[] = {
- { "kvm_trace_entryexit", "%u %p %u %u %u %u %u %u", 1, kvm_add_trace },
- { "kvm_trace_handler", "%u %p %u %u %u %u %u %u", 0, kvm_add_trace },
-};
-
-static int lost_records_get(void *data, u64 *val)
-{
- struct kvm_trace *kt = data;
-
- *val = atomic_read(&kt->lost_records);
- return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(kvm_trace_lost_ops, lost_records_get, NULL, "%llu\n");
-
-/*
- * The relay channel is used in "no-overwrite" mode, it keeps trace of how
- * many times we encountered a full subbuffer, to tell user space app the
- * lost records there were.
- */
-static int kvm_subbuf_start_callback(struct rchan_buf *buf, void *subbuf,
- void *prev_subbuf, size_t prev_padding)
-{
- struct kvm_trace *kt;
-
- if (!relay_buf_full(buf)) {
- if (!prev_subbuf) {
- /*
- * executed only once when the channel is opened
- * save metadata as first record
- */
- subbuf_start_reserve(buf, sizeof(u32));
- *(u32 *)subbuf = 0x12345678;
- }
-
- return 1;
- }
-
- kt = buf->chan->private_data;
- atomic_inc(&kt->lost_records);
-
- return 0;
-}
-
-static struct dentry *kvm_create_buf_file_callack(const char *filename,
- struct dentry *parent,
- int mode,
- struct rchan_buf *buf,
- int *is_global)
-{
- return debugfs_create_file(filename, mode, parent, buf,
- &relay_file_operations);
-}
-
-static int kvm_remove_buf_file_callback(struct dentry *dentry)
-{
- debugfs_remove(dentry);
- return 0;
-}
-
-static struct rchan_callbacks kvm_relay_callbacks = {
- .subbuf_start = kvm_subbuf_start_callback,
- .create_buf_file = kvm_create_buf_file_callack,
- .remove_buf_file = kvm_remove_buf_file_callback,
-};
-
-static int do_kvm_trace_enable(struct kvm_user_trace_setup *kuts)
-{
- struct kvm_trace *kt;
- int i, r = -ENOMEM;
-
- if (!kuts->buf_size || !kuts->buf_nr)
- return -EINVAL;
-
- kt = kzalloc(sizeof(*kt), GFP_KERNEL);
- if (!kt)
- goto err;
-
- r = -EIO;
- atomic_set(&kt->lost_records, 0);
- kt->lost_file = debugfs_create_file("lost_records", 0444, kvm_debugfs_dir,
- kt, &kvm_trace_lost_ops);
- if (!kt->lost_file)
- goto err;
-
- kt->rchan = relay_open("trace", kvm_debugfs_dir, kuts->buf_size,
- kuts->buf_nr, &kvm_relay_callbacks, kt);
- if (!kt->rchan)
- goto err;
-
- kvm_trace = kt;
-
- for (i = 0; i < ARRAY_SIZE(kvm_trace_probes); i++) {
- struct kvm_trace_probe *p = &kvm_trace_probes[i];
-
- r = marker_probe_register(p->name, p->format, p->probe_func, p);
- if (r)
- printk(KERN_INFO "Unable to register probe %s\n",
- p->name);
- }
-
- kvm_trace->trace_state = KVM_TRACE_STATE_RUNNING;
-
- return 0;
-err:
- if (kt) {
- if (kt->lost_file)
- debugfs_remove(kt->lost_file);
- if (kt->rchan)
- relay_close(kt->rchan);
- kfree(kt);
- }
- return r;
-}
-
-static int kvm_trace_enable(char __user *arg)
-{
- struct kvm_user_trace_setup kuts;
- int ret;
-
- ret = copy_from_user(&kuts, arg, sizeof(kuts));
- if (ret)
- return -EFAULT;
-
- ret = do_kvm_trace_enable(&kuts);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int kvm_trace_pause(void)
-{
- struct kvm_trace *kt = kvm_trace;
- int r = -EINVAL;
-
- if (kt == NULL)
- return r;
-
- if (kt->trace_state == KVM_TRACE_STATE_RUNNING) {
- kt->trace_state = KVM_TRACE_STATE_PAUSE;
- relay_flush(kt->rchan);
- r = 0;
- }
-
- return r;
-}
-
-void kvm_trace_cleanup(void)
-{
- struct kvm_trace *kt = kvm_trace;
- int i;
-
- if (kt == NULL)
- return;
-
- if (kt->trace_state == KVM_TRACE_STATE_RUNNING ||
- kt->trace_state == KVM_TRACE_STATE_PAUSE) {
-
- kt->trace_state = KVM_TRACE_STATE_CLEARUP;
-
- for (i = 0; i < ARRAY_SIZE(kvm_trace_probes); i++) {
- struct kvm_trace_probe *p = &kvm_trace_probes[i];
- marker_probe_unregister(p->name, p->probe_func, p);
- }
- marker_synchronize_unregister();
-
- relay_close(kt->rchan);
- debugfs_remove(kt->lost_file);
- kfree(kt);
- }
-}
-
-int kvm_trace_ioctl(unsigned int ioctl, unsigned long arg)
-{
- void __user *argp = (void __user *)arg;
- long r = -EINVAL;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- switch (ioctl) {
- case KVM_TRACE_ENABLE:
- r = kvm_trace_enable(argp);
- break;
- case KVM_TRACE_PAUSE:
- r = kvm_trace_pause();
- break;
- case KVM_TRACE_DISABLE:
- r = 0;
- kvm_trace_cleanup();
- break;
- }
-
- return r;
-}